From b39f3b7d5080ee4f196c5a72c4119e2c75c32c5b Mon Sep 17 00:00:00 2001 From: Sagar Sane Date: Tue, 5 Apr 2016 16:40:11 -0600 Subject: [PATCH] Issue #6 : Adds feature that provides a way to delete JcrJobRepository that is older than X hours from "now" * New API added : POST /grabbit/jobrepository/clean --data hours=X * All JobInstances, JobExecutions, StepExecutions and ExecutionContexts that either FAILED or COMPLETED X hours from "now", i.e., when the API was called -- will be deleted --- .../CleanJobRepositoryResource.groovy | 44 +++++++ .../grabbit/resources/ContentResource.groovy | 16 +-- .../resources/GrabbitResourceProvider.groovy | 3 + .../resources/TransactionResource.groovy | 18 +-- .../GrabbitExecutionContextDao.groovy | 40 ++++++ .../repository/GrabbitJobExecution.groovy | 2 +- .../repository/GrabbitJobExecutionDao.groovy | 39 ++++++ .../repository/GrabbitJobInstanceDao.groovy | 34 ++++++ .../repository/GrabbitStepExecutionDao.groovy | 33 +++++ ...y => JcrGrabbitExecutionContextDao.groovy} | 65 ++++++++-- ...roovy => JcrGrabbitJobExecutionDao.groovy} | 52 +++++++- ...groovy => JcrGrabbitJobInstanceDao.groovy} | 29 ++++- ...oovy => JcrGrabbitStepExecutionDao.groovy} | 36 +++++- .../JcrJobRepositoryFactoryBean.groovy | 24 ++-- .../services/CleanJobRepository.groovy | 28 +++++ .../impl/DefaultCleanJobRepository.groovy | 115 ++++++++++++++++++ .../GrabbitCleanJobRepositoryServlet.groovy | 60 +++++++++ .../spring/client-batch-job-context.xml | 2 +- ... JcrGrabbitExecutionContextDaoSpec.groovy} | 41 +++++-- ...y => JcrGrabbitJobExecutionDaoSpec.groovy} | 71 +++++++---- ...vy => JcrGrabbitJobInstanceDaoSpec.groovy} | 77 ++++++++---- ... => JcrGrabbitStepExecutionDaoSpec.groovy} | 39 +++--- ...rabbitCleanJobRepositoryServletSpec.groovy | 62 ++++++++++ 23 files changed, 800 insertions(+), 130 deletions(-) create mode 100644 grabbit/src/main/groovy/com/twcable/grabbit/resources/CleanJobRepositoryResource.groovy create mode 100644 grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitExecutionContextDao.groovy create mode 100644 grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobExecutionDao.groovy create mode 100644 grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobInstanceDao.groovy create mode 100644 grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitStepExecutionDao.groovy rename grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/{JcrExecutionContextDao.groovy => JcrGrabbitExecutionContextDao.groovy} (80%) rename grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/{JcrJobExecutionDao.groovy => JcrGrabbitJobExecutionDao.groovy} (86%) rename grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/{JcrJobInstanceDao.groovy => JcrGrabbitJobInstanceDao.groovy} (89%) rename grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/{JcrStepExecutionDao.groovy => JcrGrabbitStepExecutionDao.groovy} (87%) create mode 100644 grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/services/CleanJobRepository.groovy create mode 100644 grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/services/impl/DefaultCleanJobRepository.groovy create mode 100644 grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/servlets/GrabbitCleanJobRepositoryServlet.groovy rename grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/{JcrExecutionContextDaoSpec.groovy => JcrGrabbitExecutionContextDaoSpec.groovy} (69%) rename grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/{JcrJobExecutionDaoSpec.groovy => JcrGrabbitJobExecutionDaoSpec.groovy} (72%) rename grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/{JcrJobInstanceDaoSpec.groovy => JcrGrabbitJobInstanceDaoSpec.groovy} (53%) rename grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/{JcrStepExecutionDaoSpec.groovy => JcrGrabbitStepExecutionDaoSpec.groovy} (76%) create mode 100644 grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/servlets/GrabbitCleanJobRepositoryServletSpec.groovy diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/resources/CleanJobRepositoryResource.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/resources/CleanJobRepositoryResource.groovy new file mode 100644 index 0000000..95595ed --- /dev/null +++ b/grabbit/src/main/groovy/com/twcable/grabbit/resources/CleanJobRepositoryResource.groovy @@ -0,0 +1,44 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.resources + +import groovy.transform.CompileStatic +import org.apache.sling.api.resource.ResourceResolver +import org.apache.sling.api.resource.SyntheticResource + +import javax.annotation.Nonnull + +/** + * A resource representing what needs to be deleted from Grabbit's JobRepository. + * provided by {@link GrabbitResourceProvider}. + * Queried from {@link com.twcable.grabbit.spring.batch.repository.servlets.GrabbitCleanJobRepositoryServlet}. + */ +@CompileStatic +class CleanJobRepositoryResource extends SyntheticResource { + + public static final String CLEAN_JOBREPOSITORY_RESOURCE_TYPE = "twcable:grabbit/jobrepository/clean" + + CleanJobRepositoryResource(@Nonnull final ResourceResolver resourceResolver, @Nonnull final String resolutionPath) { + super(resourceResolver, resolutionPath, CLEAN_JOBREPOSITORY_RESOURCE_TYPE) + } + + + @Override + String getResourceType() { + return CLEAN_JOBREPOSITORY_RESOURCE_TYPE + } +} diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/resources/ContentResource.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/resources/ContentResource.groovy index 67d6366..fe855fa 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/resources/ContentResource.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/resources/ContentResource.groovy @@ -1,11 +1,3 @@ -package com.twcable.grabbit.resources - -import groovy.transform.CompileStatic -import org.apache.sling.api.resource.ResourceResolver -import org.apache.sling.api.resource.SyntheticResource - -import javax.annotation.Nonnull - /* * Copyright 2015 Time Warner Cable, Inc. * @@ -22,6 +14,14 @@ import javax.annotation.Nonnull * limitations under the License. */ +package com.twcable.grabbit.resources + +import groovy.transform.CompileStatic +import org.apache.sling.api.resource.ResourceResolver +import org.apache.sling.api.resource.SyntheticResource + +import javax.annotation.Nonnull + /** * A resource representing some content to be streamed from a Grabbit instance. * provided by {@link GrabbitResourceProvider}. diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/resources/GrabbitResourceProvider.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/resources/GrabbitResourceProvider.groovy index 26c9aa3..e6f1d4e 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/resources/GrabbitResourceProvider.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/resources/GrabbitResourceProvider.groovy @@ -79,6 +79,9 @@ class GrabbitResourceProvider implements ResourceProvider { case ~/^\/grabbit\/content(\/)?$/: log.debug "Resolving ${path} to ContentResource" return new ContentResource(resolver, path) + case ~/^\/grabbit\/jobrepository\/clean(\/)?$/: + log.debug "Resolving ${path} to CleanJobRepositoryResource" + return new CleanJobRepositoryResource(resolver, path) default: //Should provide a root resource for /grabbit, along with HATEOS style for this link, and other links. https://github.com/TWCable/grabbit/issues/22 return null diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/resources/TransactionResource.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/resources/TransactionResource.groovy index 1be1404..ecf5923 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/resources/TransactionResource.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/resources/TransactionResource.groovy @@ -1,12 +1,3 @@ -package com.twcable.grabbit.resources - -import groovy.transform.CompileStatic -import org.apache.sling.api.resource.ResourceResolver -import org.apache.sling.api.resource.SyntheticResource - -import javax.annotation.Nonnull -import java.util.regex.Matcher - /* * Copyright 2015 Time Warner Cable, Inc. * @@ -23,6 +14,15 @@ import java.util.regex.Matcher * limitations under the License. */ +package com.twcable.grabbit.resources + +import groovy.transform.CompileStatic +import org.apache.sling.api.resource.ResourceResolver +import org.apache.sling.api.resource.SyntheticResource + +import javax.annotation.Nonnull +import java.util.regex.Matcher + /** * {@link TransactionResource} represents a a logical group of jobs by configuration run. * diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitExecutionContextDao.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitExecutionContextDao.groovy new file mode 100644 index 0000000..9939e6e --- /dev/null +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitExecutionContextDao.groovy @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.spring.batch.repository + +import org.springframework.batch.core.repository.dao.ExecutionContextDao +import org.springframework.batch.item.ExecutionContext; + + +/** + * Modified DAO Interface for persisting and retrieving {@link ExecutionContext} + * @see ExecutionContextDao for more details + */ +interface GrabbitExecutionContextDao extends ExecutionContextDao { + + /** + * Returns job execution context paths by comparing "executionId" property on "executionContext/job/" with + * "executionId" property on JobExecutions for the @param jobExecutionResourcePaths + */ + public Collection getJobExecutionContextPaths(Collection jobExecutionResourcePaths) + + /** + * Returns step execution context paths by comparing "executionId" property on "executionContext/job/" with + * "id" property on StepExecutions for the @param stepExecutionResourcePaths + */ + public Collection getStepExecutionContextPaths(Collection stepExecutionResourcePaths) +} diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobExecution.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobExecution.groovy index 927d0d9..c0fa820 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobExecution.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobExecution.groovy @@ -30,7 +30,7 @@ import org.springframework.batch.core.JobExecution * be grouped/referenced by their common transactionID. *

* - * @see {@link com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao} for a good handle on how we + * @see {@link JcrGrabbitJobExecutionDao} for a good handle on how we * serve up this JobExecution during the Spring Batch lifecycle. */ @CompileStatic diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobExecutionDao.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobExecutionDao.groovy new file mode 100644 index 0000000..349ed31 --- /dev/null +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobExecutionDao.groovy @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.spring.batch.repository + +import org.springframework.batch.core.BatchStatus +import org.springframework.batch.core.JobExecution +import org.springframework.batch.core.repository.dao.JobExecutionDao + +/** + * Modified DAO Interface for persisting and retrieving {@link JobExecution} + * @see JobExecutionDao for more details + */ +interface GrabbitJobExecutionDao extends JobExecutionDao{ + + /** + * Returns job execution paths for given BatchStatuses + */ + public Collection getJobExecutions(Collection batchStatuses) + + /** + * Returns job execution paths which ended @param hours ago from "Now" + */ + public Collection getJobExecutions(int hours, Collection jobExecutionPaths) + +} diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobInstanceDao.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobInstanceDao.groovy new file mode 100644 index 0000000..032044e --- /dev/null +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitJobInstanceDao.groovy @@ -0,0 +1,34 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.spring.batch.repository + +import org.springframework.batch.core.JobInstance +import org.springframework.batch.core.repository.dao.JobInstanceDao + +/** + * Modified DAO Interface for persisting and retrieving {@link JobInstance} + * @see JobInstanceDao for more details + */ +interface GrabbitJobInstanceDao extends JobInstanceDao { + + /** + * Returns job instance paths by comparing "id" property on "jobInstances" with + * "instanceId" property on JobExecutions for the @param jobExecutionResourcePaths + */ + public Collection getJobInstancePaths(Collection jobExecutionResourcePaths) + +} diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitStepExecutionDao.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitStepExecutionDao.groovy new file mode 100644 index 0000000..7a1d42d --- /dev/null +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/GrabbitStepExecutionDao.groovy @@ -0,0 +1,33 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.spring.batch.repository + +import org.springframework.batch.core.StepExecution +import org.springframework.batch.core.repository.dao.StepExecutionDao + +/** + * Modified DAO Interface for persisting and retrieving {@link StepExecution} + * @see StepExecutionDao for more details + */ +interface GrabbitStepExecutionDao extends StepExecutionDao { + + /** + * Returns step execution paths by comparing "jobExecutionId" property on "stepExecutions with + * "executionId" property on JobExecutions for the @param jobExecutionResourcePaths + */ + public Collection getStepExecutionPaths(Collection jobExecutionResourcePaths) +} diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrExecutionContextDao.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitExecutionContextDao.groovy similarity index 80% rename from grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrExecutionContextDao.groovy rename to grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitExecutionContextDao.groovy index cd1116b..f12e06a 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrExecutionContextDao.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitExecutionContextDao.groovy @@ -20,11 +20,8 @@ import com.twcable.grabbit.jcr.JcrUtil import com.twcable.grabbit.util.CryptoUtil import groovy.transform.CompileStatic import groovy.util.logging.Slf4j -import org.apache.sling.api.resource.ModifiableValueMap -import org.apache.sling.api.resource.Resource -import org.apache.sling.api.resource.ResourceResolver -import org.apache.sling.api.resource.ResourceResolverFactory -import org.apache.sling.api.resource.ValueMap +import org.apache.sling.api.SlingException +import org.apache.sling.api.resource.* import org.springframework.batch.core.JobExecution import org.springframework.batch.core.StepExecution import org.springframework.batch.core.repository.ExecutionContextSerializer @@ -33,6 +30,7 @@ import org.springframework.batch.item.ExecutionContext import javax.annotation.Nonnull +import static JcrGrabbitStepExecutionDao.ID import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED import static org.apache.sling.api.resource.ResourceUtil.getOrCreateResource @@ -42,7 +40,7 @@ import static org.apache.sling.api.resource.ResourceUtil.getOrCreateResource */ @CompileStatic @Slf4j -class JcrExecutionContextDao extends AbstractJcrDao implements ExecutionContextDao { +class JcrGrabbitExecutionContextDao extends AbstractJcrDao implements GrabbitExecutionContextDao { public static final String EXECUTION_CONTEXT_ROOT = "${ROOT_RESOURCE_NAME}/executionContexts" public static final String JOB_EXECUTION_CONTEXT_ROOT = "${EXECUTION_CONTEXT_ROOT}/job" @@ -55,7 +53,7 @@ class JcrExecutionContextDao extends AbstractJcrDao implements ExecutionContextD private ExecutionContextSerializer contextSerializer - JcrExecutionContextDao( + JcrGrabbitExecutionContextDao( @Nonnull final ResourceResolverFactory rrf, @Nonnull final ExecutionContextSerializer serializer) { this.resourceResolverFactory = rrf this.contextSerializer = serializer @@ -156,7 +154,7 @@ class JcrExecutionContextDao extends AbstractJcrDao implements ExecutionContextD } /** - * Must be called when a new instance of JcrExecutionContextDao is created. + * Must be called when a new instance of JcrGrabbitExecutionContextDao is created. * Ensures that {@link #STEP_EXECUTION_CONTEXT_ROOT} and {@link #JOB_EXECUTION_CONTEXT_ROOT} exist on initialization */ @Override @@ -306,4 +304,55 @@ class JcrExecutionContextDao extends AbstractJcrDao implements ExecutionContextD contextAsMap.each { key, value -> context.put(key, value) } context } + + @Override + Collection getJobExecutionContextPaths(Collection jobExecutionResourcePaths) { + JcrUtil.manageResourceResolver(resourceResolverFactory) { ResourceResolver resolver -> + Collection jobExecutionContextPathsToRemove = [] + jobExecutionResourcePaths.each { String jobExecutionResourcePath -> + Resource jobExecutionResource = resolver.getResource(jobExecutionResourcePath) + ValueMap props = jobExecutionResource.adaptTo(ValueMap) + Long jobExecutionId = props[JcrGrabbitJobExecutionDao.EXECUTION_ID] as Long + String query = "select * from [nt:unstructured] as s " + + "where ISDESCENDANTNODE(s,'${JOB_EXECUTION_CONTEXT_ROOT}') AND ( s.${EXECUTION_ID} = ${jobExecutionId})" + try { + List jobExecutionContextPaths = resolver.findResources(query, "JCR-SQL2").toList().collect { it.path } + jobExecutionContextPathsToRemove.addAll(jobExecutionContextPaths) + } + catch(SlingException | IllegalStateException e) { + log.error "Exception when executing Query: ${query}. \nException - ", e + } + } + //There are 2 versions of Resources returned back by findResources + //One for JcrNodeResource and one for SocialResourceWrapper + //Hence, duplicates need to be removed + return jobExecutionContextPathsToRemove.unique() as Collection + } + } + + @Override + Collection getStepExecutionContextPaths(Collection stepExecutionResourcePaths) { + JcrUtil.manageResourceResolver(resourceResolverFactory) { ResourceResolver resolver -> + Collection stepExecutionContextPathsToRemove = [] + stepExecutionResourcePaths.each { String stepExecutionResourcePath -> + Resource stepExecutionResource = resolver.getResource(stepExecutionResourcePath) + ValueMap props = stepExecutionResource.adaptTo(ValueMap) + Long stepExecutionId = props[ID] as Long + String query = "select * from [nt:unstructured] as s " + + "where ISDESCENDANTNODE(s,'${STEP_EXECUTION_CONTEXT_ROOT}') AND ( s.${EXECUTION_ID} = ${stepExecutionId})" + try { + List stepExecutionContextPaths = resolver.findResources(query, "JCR-SQL2").toList().collect { it.path } + stepExecutionContextPathsToRemove.addAll(stepExecutionContextPaths) + } catch (SlingException | IllegalStateException e) { + log.error "Exception when executing Query: ${query}. \nException - ", e + } + } + //There are 2 versions of Resources returned back by findResources + //One for JcrNodeResource and one for SocialResourceWrapper + //Hence, duplicates need to be removed + return stepExecutionContextPathsToRemove.unique() as Collection + + } + + } } diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobExecutionDao.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobExecutionDao.groovy similarity index 86% rename from grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobExecutionDao.groovy rename to grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobExecutionDao.groovy index 3ca201f..a0d4be8 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobExecutionDao.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobExecutionDao.groovy @@ -29,7 +29,7 @@ import org.springframework.batch.core.repository.dao.JobExecutionDao import javax.annotation.Nonnull -import static JcrJobInstanceDao.JOB_INSTANCE_ROOT +import static JcrGrabbitJobInstanceDao.JOB_INSTANCE_ROOT import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED import static org.apache.sling.api.resource.ResourceUtil.getOrCreateResource @@ -39,7 +39,7 @@ import static org.apache.sling.api.resource.ResourceUtil.getOrCreateResource */ @CompileStatic @Slf4j -class JcrJobExecutionDao extends AbstractJcrDao implements JobExecutionDao { +class JcrGrabbitJobExecutionDao extends AbstractJcrDao implements GrabbitJobExecutionDao { public static final String JOB_EXECUTION_ROOT = "${ROOT_RESOURCE_NAME}/jobExecutions" @@ -59,7 +59,7 @@ class JcrJobExecutionDao extends AbstractJcrDao implements JobExecutionDao { private ResourceResolverFactory resourceResolverFactory - JcrJobExecutionDao(ResourceResolverFactory rrf) { + JcrGrabbitJobExecutionDao(ResourceResolverFactory rrf) { this.resourceResolverFactory = rrf } @@ -184,7 +184,7 @@ class JcrJobExecutionDao extends AbstractJcrDao implements JobExecutionDao { } /** - * Returns JobParameters using the given instanceId from {@link JcrJobInstanceDao#JOB_INSTANCE_ROOT} + * Returns JobParameters using the given instanceId from {@link JcrGrabbitJobInstanceDao#JOB_INSTANCE_ROOT} * * @see #mapJobExecution(ResourceResolver, ValueMap, JobInstance) */ @@ -193,7 +193,7 @@ class JcrJobExecutionDao extends AbstractJcrDao implements JobExecutionDao { final instanceResource = resolver.getResource(jobInstanceRoot, "${instanceId}") final properties = instanceResource.adaptTo(ValueMap) - final params = properties[JcrJobInstanceDao.PARAMETERS] as String[] + final params = properties[JcrGrabbitJobInstanceDao.PARAMETERS] as String[] final paramsMap = params?.collectEntries { String entry -> final pair = entry.split("=") @@ -365,7 +365,7 @@ class JcrJobExecutionDao extends AbstractJcrDao implements JobExecutionDao { } /** - * Must be called when a new instance of JcrJobExecutionDao is created. + * Must be called when a new instance of JcrGrabbitJobExecutionDao is created. * Ensures that {@link #JOB_EXECUTION_ROOT} exists on initialization */ @Override @@ -381,4 +381,44 @@ class JcrJobExecutionDao extends AbstractJcrDao implements JobExecutionDao { } } } + + @Override + Collection getJobExecutions(Collection batchStatuses) { + String statusPredicate = batchStatuses.collect { "s.status = '${it}'" }.join(' or ') + JcrUtil.manageResourceResolver(resourceResolverFactory) { ResourceResolver resolver -> + String jobExecutionsQuery = "select * from [nt:unstructured] as s where " + + "ISDESCENDANTNODE(s,'${JOB_EXECUTION_ROOT}') AND ( ${statusPredicate} )" + Collection jobExecutions = resolver.findResources(jobExecutionsQuery, "JCR-SQL2") + .toList() + .collect { it.path } + .unique() as Collection + log.debug "JobExecutions: $jobExecutions, size: ${jobExecutions.size()}" + return jobExecutions + } + + } + + @Override + Collection getJobExecutions(int hours, Collection jobExecutions) { + JcrUtil.manageResourceResolver(resourceResolverFactory) { ResourceResolver resolver -> + //Create a Date object that is "hours" ago from now + Calendar olderThanHours = Calendar.getInstance() + log.info "Current time: ${olderThanHours.time}" + olderThanHours.add(Calendar.HOUR, -hours) + log.info "Hours ${hours} .. OlderThanHours Time: ${olderThanHours.time}" + + //Find all resources that are older than "olderThanHours" Date + Collection olderResourcePaths = jobExecutions.findAll { String resourcePath -> + Resource resource = resolver.getResource(resourcePath) + ValueMap props = resource.adaptTo(ValueMap) + String dateInIsoString = props[END_TIME] as String + Date endTimeDate = DateUtil.getDateFromISOString(dateInIsoString) + olderThanHours.time.compareTo(endTimeDate) > 0 + } as Collection + log.debug "JobExecutionsOlder than ${hours} hours: $olderResourcePaths , length: ${olderResourcePaths.size()}" + return olderResourcePaths + + } + + } } diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobInstanceDao.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobInstanceDao.groovy similarity index 89% rename from grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobInstanceDao.groovy rename to grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobInstanceDao.groovy index e5f5ca5..97c49a4 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobInstanceDao.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobInstanceDao.groovy @@ -41,7 +41,7 @@ import static org.apache.sling.api.resource.ResourceUtil.getOrCreateResource */ @CompileStatic @Slf4j -class JcrJobInstanceDao extends AbstractJcrDao implements JobInstanceDao { +class JcrGrabbitJobInstanceDao extends AbstractJcrDao implements GrabbitJobInstanceDao { public static final String JOB_INSTANCE_ROOT = "${ROOT_RESOURCE_NAME}/jobInstances" @@ -54,7 +54,7 @@ class JcrJobInstanceDao extends AbstractJcrDao implements JobInstanceDao { private ResourceResolverFactory resourceResolverFactory - JcrJobInstanceDao(ResourceResolverFactory rrf) { + JcrGrabbitJobInstanceDao(ResourceResolverFactory rrf) { this.resourceResolverFactory = rrf } @@ -235,7 +235,7 @@ class JcrJobInstanceDao extends AbstractJcrDao implements JobInstanceDao { } /** - * Must be called when a new instance of JcrJobInstanceDao is created. + * Must be called when a new instance of JcrGrabbitJobInstanceDao is created. * Ensures that {@link #JOB_INSTANCE_ROOT} exists on initialization */ @Override @@ -262,4 +262,27 @@ class JcrJobInstanceDao extends AbstractJcrDao implements JobInstanceDao { nextId } + + @Override + Collection getJobInstancePaths(Collection jobExecutionResourcePaths) { + JcrUtil.manageResourceResolver(resourceResolverFactory) { ResourceResolver resolver -> + Collection jobInstancesToRemove = [] + jobExecutionResourcePaths.each { String jobExecutionResourcePath -> + Resource jobExecutionResource = resolver.getResource(jobExecutionResourcePath) + ValueMap props = jobExecutionResource.adaptTo(ValueMap) + Long instanceId = props[JcrGrabbitJobExecutionDao.INSTANCE_ID] as Long + String jobInstanceToRemoveResourcePath = "${JOB_INSTANCE_ROOT}/${instanceId}".toString() + Resource jobInstanceToRemove = resolver.getResource(jobInstanceToRemoveResourcePath) + if (!jobInstanceToRemove) { + log.info "JobInstance with id : ${instanceId} is already removed" + } else { + jobInstancesToRemove.add(jobInstanceToRemoveResourcePath) + } + } + return jobInstancesToRemove + + + } + + } } diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrStepExecutionDao.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitStepExecutionDao.groovy similarity index 87% rename from grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrStepExecutionDao.groovy rename to grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitStepExecutionDao.groovy index 0ecd769..d8b56e7 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrStepExecutionDao.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitStepExecutionDao.groovy @@ -21,6 +21,7 @@ import com.twcable.grabbit.jcr.JcrUtil import com.twcable.grabbit.util.CryptoUtil import groovy.transform.CompileStatic import groovy.util.logging.Slf4j +import org.apache.sling.api.SlingException import org.apache.sling.api.resource.ModifiableValueMap import org.apache.sling.api.resource.Resource import org.apache.sling.api.resource.ResourceResolver @@ -34,6 +35,7 @@ import org.springframework.batch.core.repository.dao.StepExecutionDao import javax.annotation.Nonnull +import static JcrGrabbitJobExecutionDao.EXECUTION_ID import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED import static org.apache.sling.api.resource.ResourceUtil.getOrCreateResource @@ -43,7 +45,7 @@ import static org.apache.sling.api.resource.ResourceUtil.getOrCreateResource */ @CompileStatic @Slf4j -public class JcrStepExecutionDao extends AbstractJcrDao implements StepExecutionDao { +public class JcrGrabbitStepExecutionDao extends AbstractJcrDao implements GrabbitStepExecutionDao { public static final String STEP_EXECUTION_ROOT = "${ROOT_RESOURCE_NAME}/stepExecutions" @@ -69,7 +71,7 @@ public class JcrStepExecutionDao extends AbstractJcrDao implements StepExecution private ResourceResolverFactory resourceResolverFactory - JcrStepExecutionDao(ResourceResolverFactory rrf) { + JcrGrabbitStepExecutionDao(ResourceResolverFactory rrf) { this.resourceResolverFactory = rrf } @@ -279,7 +281,7 @@ public class JcrStepExecutionDao extends AbstractJcrDao implements StepExecution } /** - * Must be called when a new instance of JcrStepExecutionDao is created. + * Must be called when a new instance of JcrGrabbitStepExecutionDao is created. * Ensures that {@link #STEP_EXECUTION_ROOT} exists on initialization */ @Override @@ -290,10 +292,34 @@ public class JcrStepExecutionDao extends AbstractJcrDao implements StepExecution //create the Root Resource throw new IllegalStateException("Cannot get or create RootResource for : ${STEP_EXECUTION_ROOT}") } - if (!getOrCreateResource(resolver, JcrJobExecutionDao.JOB_EXECUTION_ROOT, NT_UNSTRUCTURED, NT_UNSTRUCTURED, true)) { + if (!getOrCreateResource(resolver, JcrGrabbitJobExecutionDao.JOB_EXECUTION_ROOT, NT_UNSTRUCTURED, NT_UNSTRUCTURED, true)) { //create the Root Resource - throw new IllegalStateException("Cannot get or create RootResource for : ${JcrJobExecutionDao.JOB_EXECUTION_ROOT}") + throw new IllegalStateException("Cannot get or create RootResource for : ${JcrGrabbitJobExecutionDao.JOB_EXECUTION_ROOT}") } } } + + @Override + Collection getStepExecutionPaths(Collection jobExecutionResourcePaths) { + JcrUtil.manageResourceResolver(resourceResolverFactory) { ResourceResolver resolver -> + Collection stepExecutionsToRemove = [] + jobExecutionResourcePaths.each { String jobExecutionResourcePath -> + Resource jobExecutionResource = resolver.getResource(jobExecutionResourcePath) + ValueMap props = jobExecutionResource.adaptTo(ValueMap) + Long jobExecutionId = props[EXECUTION_ID] as Long + String query = "select * from [nt:unstructured] as s " + + "where ISDESCENDANTNODE(s,'${STEP_EXECUTION_ROOT}') AND ( s.${JOB_EXECUTION_ID} = ${jobExecutionId})" + try { + List stepExecutions = resolver.findResources(query, "JCR-SQL2").toList().collect { it.path } + stepExecutionsToRemove.addAll(stepExecutions) + } catch (SlingException | IllegalStateException e) { + log.error "Exception when executing Query: ${query}. \nException - ", e + } + } + //There are 2 versions of Resources returned back by findResources + //One for JcrNodeResource and one for SocialResourceWrapper + //Hence, duplicates need to be removed + return stepExecutionsToRemove.unique() as Collection + } + } } diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobRepositoryFactoryBean.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobRepositoryFactoryBean.groovy index 4f7f743..fada65d 100644 --- a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobRepositoryFactoryBean.groovy +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobRepositoryFactoryBean.groovy @@ -39,13 +39,13 @@ import org.springframework.transaction.PlatformTransactionManager @Slf4j class JcrJobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean { - private JcrJobExecutionDao jobExecutionDao + private JcrGrabbitJobExecutionDao jobExecutionDao - private JcrJobInstanceDao jobInstanceDao + private JcrGrabbitJobInstanceDao jobInstanceDao - private JcrStepExecutionDao stepExecutionDao + private JcrGrabbitStepExecutionDao stepExecutionDao - private JcrExecutionContextDao executionContextDao + private JcrGrabbitExecutionContextDao executionContextDao private ResourceResolverFactory resourceResolverFactory @@ -76,22 +76,22 @@ class JcrJobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean { } - JobExecutionDao getJobExecutionDao() { + GrabbitJobExecutionDao getJobExecutionDao() { jobExecutionDao } - JobInstanceDao getJobInstanceDao() { + GrabbitJobInstanceDao getJobInstanceDao() { jobInstanceDao } - StepExecutionDao getStepExecutionDao() { + GrabbitStepExecutionDao getStepExecutionDao() { stepExecutionDao } - ExecutionContextDao getExecutionContextDao() { + GrabbitExecutionContextDao getExecutionContextDao() { executionContextDao } @@ -99,7 +99,7 @@ class JcrJobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean { @Override protected JobInstanceDao createJobInstanceDao() throws Exception { log.info "Create JobInstance" - jobInstanceDao = new JcrJobInstanceDao(resourceResolverFactory) + jobInstanceDao = new JcrGrabbitJobInstanceDao(resourceResolverFactory) jobInstanceDao.ensureRootResource() jobInstanceDao } @@ -108,7 +108,7 @@ class JcrJobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean { @Override protected JobExecutionDao createJobExecutionDao() throws Exception { log.info "Create JobExecution" - jobExecutionDao = new JcrJobExecutionDao(resourceResolverFactory) + jobExecutionDao = new JcrGrabbitJobExecutionDao(resourceResolverFactory) jobExecutionDao.ensureRootResource() jobExecutionDao } @@ -117,7 +117,7 @@ class JcrJobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean { @Override protected StepExecutionDao createStepExecutionDao() throws Exception { log.info "Create StepExecution" - stepExecutionDao = new JcrStepExecutionDao(resourceResolverFactory) + stepExecutionDao = new JcrGrabbitStepExecutionDao(resourceResolverFactory) stepExecutionDao.ensureRootResource() stepExecutionDao } @@ -126,7 +126,7 @@ class JcrJobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean { @Override protected ExecutionContextDao createExecutionContextDao() throws Exception { log.info "Create ExecutionContext" - executionContextDao = new JcrExecutionContextDao(resourceResolverFactory, executionContextSerializer) + executionContextDao = new JcrGrabbitExecutionContextDao(resourceResolverFactory, executionContextSerializer) executionContextDao.ensureRootResource() executionContextDao } diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/services/CleanJobRepository.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/services/CleanJobRepository.groovy new file mode 100644 index 0000000..859c83b --- /dev/null +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/services/CleanJobRepository.groovy @@ -0,0 +1,28 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.spring.batch.repository.services + +interface CleanJobRepository { + + /** + * This API is used to clean Grabbit's JCR Job Repository. It removes all job executions and all associated + * job instances etc. that are @param hours older than NOW (time when this API is called) + * @return Collection of JobExecutionIds that were removed + */ + public Collection cleanJobRepository(int hours) + +} diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/services/impl/DefaultCleanJobRepository.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/services/impl/DefaultCleanJobRepository.groovy new file mode 100644 index 0000000..1de38ae --- /dev/null +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/services/impl/DefaultCleanJobRepository.groovy @@ -0,0 +1,115 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.spring.batch.repository.services.impl + +import com.twcable.grabbit.jcr.JcrUtil +import com.twcable.grabbit.spring.batch.repository.JcrJobRepositoryFactoryBean +import com.twcable.grabbit.spring.batch.repository.services.CleanJobRepository +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.apache.felix.scr.annotations.Activate +import org.apache.felix.scr.annotations.Component +import org.apache.felix.scr.annotations.Reference +import org.apache.felix.scr.annotations.Service +import org.apache.sling.api.resource.PersistenceException +import org.apache.sling.api.resource.Resource +import org.apache.sling.api.resource.ResourceResolver +import org.apache.sling.api.resource.ResourceResolverFactory +import org.springframework.batch.core.BatchStatus +import org.springframework.context.ConfigurableApplicationContext + +@Slf4j +@CompileStatic +@Component(label = "Grabbit Clean Job Repository Service", description = "Grabbit Clean Job Repository Service", immediate = true, metatype = true, enabled = true) +@Service(CleanJobRepository) +@SuppressWarnings(['GroovyUnusedDeclaration', 'GrMethodMayBeStatic']) +class DefaultCleanJobRepository implements CleanJobRepository { + + @Reference + ResourceResolverFactory resourceResolverFactory + + @Reference + ConfigurableApplicationContext configurableApplicationContext + + @Activate + void activate() { + log.info "CleanJobRepository Service activated" + } + + @Override + Collection cleanJobRepository(int hours) { + JcrJobRepositoryFactoryBean jobRepositoryFactoryBean = configurableApplicationContext.getBean(JcrJobRepositoryFactoryBean) + + if(!jobRepositoryFactoryBean) { + log.error "Cannot get an instance of JcrJobRepositoryFactoryBean. Will not clean up Grabbit Jcr Job Repository" + return [] + } + + Collection jobExecutionPaths = jobRepositoryFactoryBean.jobExecutionDao.getJobExecutions([BatchStatus.FAILED, BatchStatus.COMPLETED]) + Collection olderThanHoursJobExecutions = jobRepositoryFactoryBean.jobExecutionDao.getJobExecutions(hours, jobExecutionPaths) + Collection jobInstancesToRemove = jobRepositoryFactoryBean.jobInstanceDao.getJobInstancePaths(olderThanHoursJobExecutions) + Collection stepExecutionsToRemove = jobRepositoryFactoryBean.stepExecutionDao.getStepExecutionPaths(olderThanHoursJobExecutions) + Collection jobExecutionContextsToRemove = jobRepositoryFactoryBean.executionContextDao.getJobExecutionContextPaths(olderThanHoursJobExecutions) + Collection stepExecutionContextsToRemove = jobRepositoryFactoryBean.executionContextDao.getStepExecutionContextPaths(stepExecutionsToRemove) + + JcrUtil.manageResourceResolver(resourceResolverFactory) { ResourceResolver resolver -> + + log.debug "jobInstancesToRemove: $jobInstancesToRemove, size: ${jobInstancesToRemove.size()}" + log.debug "jobExecutionsToRemove: $olderThanHoursJobExecutions, size: ${olderThanHoursJobExecutions.size()}" + log.debug "stepExecutionsToRemove: $stepExecutionsToRemove, size: ${stepExecutionsToRemove.size()}" + log.debug "jobExecutionContextsToRemove: $jobExecutionContextsToRemove, size: ${jobExecutionContextsToRemove.size()}" + log.debug "stepExecutionContextsToResource: $stepExecutionContextsToRemove, size: ${stepExecutionContextsToRemove.size()}" + + log.info "Removing ${jobInstancesToRemove.size()} JobInstances" + removeResources(jobInstancesToRemove, resolver) + log.info "Removing ${olderThanHoursJobExecutions.size()} JobExecutions" + removeResources(olderThanHoursJobExecutions, resolver) + log.info "Removing ${stepExecutionsToRemove.size()} StepExecutions" + removeResources(stepExecutionsToRemove, resolver) + log.info "Removing ${jobExecutionContextsToRemove.size()} JobExecutionContexts" + removeResources(jobExecutionContextsToRemove, resolver) + log.info "Removing ${stepExecutionContextsToRemove.size()} StepExecutionContexts" + removeResources(stepExecutionContextsToRemove, resolver) + } + + Collection removedJobExecutionIds = olderThanHoursJobExecutions.collect { it.split("/").last() } + return removedJobExecutionIds + } + + private void removeResources(Collection resourcePathsToRemove, ResourceResolver resolver) { + try { + resourcePathsToRemove.each { + Resource resourceToDelete = resolver.getResource(it) + if(resourceToDelete) { + resolver.delete(resourceToDelete) + log.debug "Resource ${it} will be removed" + } + else { + log.warn "Resource ${it} doesn't exist. So cannot remove it" + } + } + resolver.commit() + resolver.refresh() + } + catch(PersistenceException e) { + log.error "Exception while removing resources :", e + if(resolver.hasChanges()) { + resolver.revert() + } + } + } +} diff --git a/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/servlets/GrabbitCleanJobRepositoryServlet.groovy b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/servlets/GrabbitCleanJobRepositoryServlet.groovy new file mode 100644 index 0000000..53f515e --- /dev/null +++ b/grabbit/src/main/groovy/com/twcable/grabbit/spring/batch/repository/servlets/GrabbitCleanJobRepositoryServlet.groovy @@ -0,0 +1,60 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.spring.batch.repository.servlets + +import com.twcable.grabbit.spring.batch.repository.services.CleanJobRepository +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.apache.felix.scr.annotations.Reference +import org.apache.felix.scr.annotations.sling.SlingServlet +import org.apache.sling.api.SlingHttpServletRequest +import org.apache.sling.api.SlingHttpServletResponse +import org.apache.sling.api.servlets.SlingAllMethodsServlet +import org.apache.sling.api.servlets.SlingSafeMethodsServlet + +import javax.annotation.Nonnull +import javax.servlet.http.HttpServletResponse + +/** + * This servlet is used for cleanup of the Grabbit's JobRepository stored under /var/grabbit/job. + * + * It is a handler for the {@link com.twcable.grabbit.resources.CleanJobRepositoryResource} resource. + */ +@Slf4j +@CompileStatic +@SlingServlet(methods = ['POST'], resourceTypes = ['twcable:grabbit/jobrepository/clean']) +class GrabbitCleanJobRepositoryServlet extends SlingAllMethodsServlet { + + @Reference + CleanJobRepository cleanJobRepository + + @Override + protected void doPost( @Nonnull SlingHttpServletRequest request, @Nonnull SlingHttpServletResponse response) { + String hoursParam = request.getParameter("hours") ?: "" + if(!hoursParam.isInteger()) { + log.warn "Parameter 'hours' must be an integer" + response.status = HttpServletResponse.SC_BAD_REQUEST + response.writer.write("Parameter 'hours' must be an integer") + return + } + int hours = hoursParam.toInteger() + Collection removedJobExecutions = cleanJobRepository.cleanJobRepository(hours) + response.status = HttpServletResponse.SC_OK + response.writer.write ("JobExecutions and the corresponding JobInstances, StepExecutions and ExecutionContexts " + + "were removed. JobExecutionsIds that were removed: ${removedJobExecutions}") + } +} diff --git a/grabbit/src/main/resources/META-INF/spring/client-batch-job-context.xml b/grabbit/src/main/resources/META-INF/spring/client-batch-job-context.xml index 5656c54..fbf72d0 100644 --- a/grabbit/src/main/resources/META-INF/spring/client-batch-job-context.xml +++ b/grabbit/src/main/resources/META-INF/spring/client-batch-job-context.xml @@ -69,7 +69,7 @@ - + diff --git a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrExecutionContextDaoSpec.groovy b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitExecutionContextDaoSpec.groovy similarity index 69% rename from grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrExecutionContextDaoSpec.groovy rename to grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitExecutionContextDaoSpec.groovy index f9dff2b..e0a9bdb 100644 --- a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrExecutionContextDaoSpec.groovy +++ b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitExecutionContextDaoSpec.groovy @@ -21,18 +21,17 @@ import org.apache.sling.api.resource.ResourceResolverFactory import org.springframework.batch.core.JobExecution import org.springframework.batch.core.StepExecution import org.springframework.batch.core.repository.ExecutionContextSerializer +import spock.lang.Ignore import spock.lang.Shared import spock.lang.Specification import spock.lang.Subject -import static com.twcable.grabbit.spring.batch.repository.JcrExecutionContextDao.EXECUTION_CONTEXT -import static com.twcable.grabbit.spring.batch.repository.JcrExecutionContextDao.EXECUTION_ID -import static com.twcable.jackalope.JCRBuilder.node -import static com.twcable.jackalope.JCRBuilder.property -import static com.twcable.jackalope.JCRBuilder.repository +import static JcrGrabbitExecutionContextDao.EXECUTION_CONTEXT +import static JcrGrabbitExecutionContextDao.EXECUTION_ID +import static com.twcable.jackalope.JCRBuilder.* -@Subject(JcrExecutionContextDao) -class JcrExecutionContextDaoSpec extends Specification { +@Subject(JcrGrabbitExecutionContextDao) +class JcrGrabbitExecutionContextDaoSpec extends Specification { @Shared ResourceResolverFactory mockFactory @@ -70,9 +69,9 @@ class JcrExecutionContextDaoSpec extends Specification { } - def "EnsureRootResource for JcrExecutionContextDao"() { + def "EnsureRootResource for JcrGrabbitExecutionContextDao"() { when: - final executionContextDao = new JcrExecutionContextDao(mockFactory, stubSerializer) + final executionContextDao = new JcrGrabbitExecutionContextDao(mockFactory, stubSerializer) executionContextDao.ensureRootResource() then: @@ -83,7 +82,7 @@ class JcrExecutionContextDaoSpec extends Specification { def "GetExecutionContext for a JobExecution"() { when: - final executionContextDao = new JcrExecutionContextDao(mockFactory, stubSerializer) + final executionContextDao = new JcrGrabbitExecutionContextDao(mockFactory, stubSerializer) final result = executionContextDao.getExecutionContext(new JobExecution(1)) then: @@ -94,7 +93,7 @@ class JcrExecutionContextDaoSpec extends Specification { def "GetExecutionContext for a StepExecution"() { when: - final executionContextDao = new JcrExecutionContextDao(mockFactory, stubSerializer) + final executionContextDao = new JcrGrabbitExecutionContextDao(mockFactory, stubSerializer) final result = executionContextDao.getExecutionContext(new StepExecution("someStep", new JobExecution(1), 1)) then: @@ -102,6 +101,26 @@ class JcrExecutionContextDaoSpec extends Specification { result.containsKey("deserialized") } + @Ignore('TODO: Implement this test when Jackalope implements resourceResolver.findResources() API') + def "GetJobExecutionContextPaths for JobExecutionPaths"() { + when: + final executionContextDao = new JcrGrabbitExecutionContextDao(mockFactory, stubSerializer) + final result = executionContextDao.getJobExecutionContextPaths([]) + + then: + 1 == 1 + } + + @Ignore('TODO: Implement this test when Jackalope implements resourceResolver.findResources() API') + def "GetStepExecutionContextPaths for JobExecutionPaths"() { + when: + final executionContextDao = new JcrGrabbitExecutionContextDao(mockFactory, stubSerializer) + final result = executionContextDao.getStepExecutionContextPaths([]) + + then: + 1 == 1 + + } class StubExecutionContextSerializer implements ExecutionContextSerializer { diff --git a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobExecutionDaoSpec.groovy b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobExecutionDaoSpec.groovy similarity index 72% rename from grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobExecutionDaoSpec.groovy rename to grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobExecutionDaoSpec.groovy index 9095368..3751ccc 100644 --- a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobExecutionDaoSpec.groovy +++ b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobExecutionDaoSpec.groovy @@ -25,22 +25,11 @@ import spock.lang.Shared import spock.lang.Specification import spock.lang.Subject -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.CREATE_TIME -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.END_TIME -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.EXECUTION_ID -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.EXIT_CODE -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.EXIT_MESSAGE -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.INSTANCE_ID -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.JOB_NAME -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.STATUS -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.TRANSACTION_ID -import static com.twcable.grabbit.spring.batch.repository.JcrJobExecutionDao.VERSION -import static com.twcable.jackalope.JCRBuilder.node -import static com.twcable.jackalope.JCRBuilder.property -import static com.twcable.jackalope.JCRBuilder.repository - -@Subject(JcrJobExecutionDao) -class JcrJobExecutionDaoSpec extends Specification { +import static JcrGrabbitJobExecutionDao.* +import static com.twcable.jackalope.JCRBuilder.* + +@Subject(JcrGrabbitJobExecutionDao) +class JcrGrabbitJobExecutionDaoSpec extends Specification { @Shared ResourceResolverFactory mockFactory @@ -86,7 +75,20 @@ class JcrJobExecutionDaoSpec extends Specification { property(CREATE_TIME, "2014-12-29T16:59:18.669-05:00"), property(END_TIME, "NULL"), property(JOB_NAME, "someOtherJob") - ) + ), + node("4", + property(INSTANCE_ID, 1), + property(EXECUTION_ID, 1), + property(TRANSACTION_ID, 5), + property(STATUS, "FAILED"), + property(EXIT_CODE, "code"), + property(EXIT_MESSAGE, "message"), + property(CREATE_TIME, "2014-12-27T16:59:18.669-05:00"), + property(END_TIME, "2015-12-29T16:59:18.669-05:00"), + property(JOB_NAME, "someJob"), + property(VERSION, 1) + + ), ), node("jobInstances", node("1")) @@ -98,9 +100,9 @@ class JcrJobExecutionDaoSpec extends Specification { } - def "EnsureRootResource for JcrJobExecutionDao"() { + def "EnsureRootResource for JcrGrabbitJobExecutionDao"() { when: - final jobExecutionDao = new JcrJobExecutionDao(mockFactory) + final jobExecutionDao = new JcrGrabbitJobExecutionDao(mockFactory) jobExecutionDao.ensureRootResource() then: @@ -111,19 +113,19 @@ class JcrJobExecutionDaoSpec extends Specification { def "FindJobExecutions for given JobInstance"() { when: - final jobExecutionDao = new JcrJobExecutionDao(mockFactory) + final jobExecutionDao = new JcrGrabbitJobExecutionDao(mockFactory) final result = jobExecutionDao.findJobExecutions(new JobInstance(1, "someJob")) then: result != null - result.size() == 2 + result.size() == 3 result.first().id == 2 } def "GetLastJobExecution for given JobInstance"() { when: - final jobExecutionDao = new JcrJobExecutionDao(mockFactory) + final jobExecutionDao = new JcrGrabbitJobExecutionDao(mockFactory) final result = jobExecutionDao.getLastJobExecution(new JobInstance(1, "someJob")) then: @@ -135,7 +137,7 @@ class JcrJobExecutionDaoSpec extends Specification { def "FindRunningJobExecutions for given Job Name"() { when: - final jobExecutionDao = new JcrJobExecutionDao(mockFactory) + final jobExecutionDao = new JcrGrabbitJobExecutionDao(mockFactory) final result = jobExecutionDao.findRunningJobExecutions("someJob") then: @@ -147,7 +149,7 @@ class JcrJobExecutionDaoSpec extends Specification { def "GetJobExecution for given JobExecution id"() { when: - final jobExecutionDao = new JcrJobExecutionDao(mockFactory) + final jobExecutionDao = new JcrGrabbitJobExecutionDao(mockFactory) final result = jobExecutionDao.getJobExecution(2) then: @@ -160,7 +162,7 @@ class JcrJobExecutionDaoSpec extends Specification { def "Get a transaction ID for a given job execution"() { when: - final jobExecutionDao = new JcrJobExecutionDao(mockFactory) + final jobExecutionDao = new JcrGrabbitJobExecutionDao(mockFactory) final jobExecution = jobExecutionDao.getJobExecution(2) as GrabbitJobExecution then: @@ -170,7 +172,7 @@ class JcrJobExecutionDaoSpec extends Specification { def "SynchronizeStatus for a given JobExecution"() { when: - final jobExecutionDao = new JcrJobExecutionDao(mockFactory) + final jobExecutionDao = new JcrGrabbitJobExecutionDao(mockFactory) def unsyncronized = new JobExecution(1) unsyncronized.setVersion(0) unsyncronized.setStatus(BatchStatus.STARTED) @@ -178,6 +180,21 @@ class JcrJobExecutionDaoSpec extends Specification { then: unsyncronized.version == 1 - unsyncronized.status == BatchStatus.COMPLETED + unsyncronized.status == BatchStatus.FAILED + } + + def "GetJobExecutions for hours and jobExecutions"() { + when: + final jobExecutionDao = new JcrGrabbitJobExecutionDao(mockFactory) + final jobExecutionPaths = [ + "/var/grabbit/job/repository/jobExecutions/1", + "/var/grabbit/job/repository/jobExecutions/4" + ] + final result = jobExecutionDao.getJobExecutions(1, jobExecutionPaths) + + then: + result != null + result.size() == 2 } + } diff --git a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobInstanceDaoSpec.groovy b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobInstanceDaoSpec.groovy similarity index 53% rename from grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobInstanceDaoSpec.groovy rename to grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobInstanceDaoSpec.groovy index f7e5b9a..48e91fe 100644 --- a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrJobInstanceDaoSpec.groovy +++ b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitJobInstanceDaoSpec.groovy @@ -18,25 +18,17 @@ package com.twcable.grabbit.spring.batch.repository import com.twcable.jackalope.impl.sling.SimpleResourceResolverFactory import org.apache.sling.api.resource.ResourceResolverFactory -import org.springframework.batch.core.DefaultJobKeyGenerator -import org.springframework.batch.core.JobExecution -import org.springframework.batch.core.JobInstance -import org.springframework.batch.core.JobParameter -import org.springframework.batch.core.JobParameters +import org.springframework.batch.core.* import spock.lang.Shared import spock.lang.Specification import spock.lang.Subject import spock.lang.Unroll -import static com.twcable.grabbit.spring.batch.repository.JcrJobInstanceDao.INSTANCE_ID -import static com.twcable.grabbit.spring.batch.repository.JcrJobInstanceDao.KEY -import static com.twcable.grabbit.spring.batch.repository.JcrJobInstanceDao.NAME -import static com.twcable.jackalope.JCRBuilder.node -import static com.twcable.jackalope.JCRBuilder.property -import static com.twcable.jackalope.JCRBuilder.repository +import static JcrGrabbitJobInstanceDao.* +import static com.twcable.jackalope.JCRBuilder.* -@Subject(JcrJobInstanceDao) -class JcrJobInstanceDaoSpec extends Specification { +@Subject(JcrGrabbitJobInstanceDao) +class JcrGrabbitJobInstanceDaoSpec extends Specification { @Shared ResourceResolverFactory mockFactory @@ -66,7 +58,34 @@ class JcrJobInstanceDaoSpec extends Specification { property(INSTANCE_ID, 4), property(NAME, "someOtherJob"), ) - ) + ), + node("jobExecutions", + node("1", + property(JcrGrabbitJobExecutionDao.INSTANCE_ID, 1), + property(JcrGrabbitJobExecutionDao.EXECUTION_ID, 1), + property(JcrGrabbitJobExecutionDao.TRANSACTION_ID, 5), + property(JcrGrabbitJobExecutionDao.STATUS, "COMPLETED"), + property(JcrGrabbitJobExecutionDao.EXIT_CODE, "code"), + property(JcrGrabbitJobExecutionDao.EXIT_MESSAGE, "message"), + property(JcrGrabbitJobExecutionDao.CREATE_TIME, "2014-12-27T16:59:18.669-05:00"), + property(JcrGrabbitJobExecutionDao.END_TIME, "2014-12-29T16:59:18.669-05:00"), + property(JcrGrabbitJobExecutionDao.JOB_NAME, "someJob"), + property(JcrGrabbitJobExecutionDao.VERSION, 1) + ), + node("4", + property(JcrGrabbitJobExecutionDao.INSTANCE_ID, 4), + property(JcrGrabbitJobExecutionDao.EXECUTION_ID, 1), + property(JcrGrabbitJobExecutionDao.TRANSACTION_ID, 5), + property(JcrGrabbitJobExecutionDao.STATUS, "FAILED"), + property(JcrGrabbitJobExecutionDao.EXIT_CODE, "code"), + property(JcrGrabbitJobExecutionDao.EXIT_MESSAGE, "message"), + property(JcrGrabbitJobExecutionDao.CREATE_TIME, "2014-12-27T16:59:18.669-05:00"), + property(JcrGrabbitJobExecutionDao.END_TIME, "2015-12-29T16:59:18.669-05:00"), + property(JcrGrabbitJobExecutionDao.JOB_NAME, "someJob"), + property(JcrGrabbitJobExecutionDao.VERSION, 1) + + ), + ), ) ) ) @@ -75,9 +94,9 @@ class JcrJobInstanceDaoSpec extends Specification { } - def "EnsureRootResource for JcrJobInstanceDao"() { + def "EnsureRootResource for JcrGrabbitJobInstanceDao"() { when: - final jobInstanceDao = new JcrJobInstanceDao(mockFactory) + final jobInstanceDao = new JcrGrabbitJobInstanceDao(mockFactory) jobInstanceDao.ensureRootResource() then: @@ -87,7 +106,7 @@ class JcrJobInstanceDaoSpec extends Specification { def "GetJobInstance for given JobExecution"() { when: - final jobInstanceDao = new JcrJobInstanceDao(mockFactory) + final jobInstanceDao = new JcrGrabbitJobInstanceDao(mockFactory) final result = jobInstanceDao.getJobInstance(new JobExecution(new JobInstance(1, "someJob"), new JobParameters())) then: @@ -99,7 +118,7 @@ class JcrJobInstanceDaoSpec extends Specification { def "GetJobInstance for given InstanceId"() { when: - final jobInstanceDao = new JcrJobInstanceDao(mockFactory) + final jobInstanceDao = new JcrGrabbitJobInstanceDao(mockFactory) final result = jobInstanceDao.getJobInstance(1) then: @@ -111,7 +130,7 @@ class JcrJobInstanceDaoSpec extends Specification { def "GetJobInstance for given Job Name and Job parameters"() { when: - final jobInstanceDao = new JcrJobInstanceDao(mockFactory) + final jobInstanceDao = new JcrGrabbitJobInstanceDao(mockFactory) final result = jobInstanceDao.getJobInstance("someJob", new JobParameters([someKey: new JobParameter("someValue")])) then: @@ -123,7 +142,7 @@ class JcrJobInstanceDaoSpec extends Specification { @Unroll def "GetJobInstances for given Job Name #jobName, a start index and count"() { when: - final jobInstanceDao = new JcrJobInstanceDao(mockFactory) + final jobInstanceDao = new JcrGrabbitJobInstanceDao(mockFactory) final result = jobInstanceDao.getJobInstances(jobName, 0, Integer.MAX_VALUE) then: @@ -140,10 +159,26 @@ class JcrJobInstanceDaoSpec extends Specification { def "GetJobNames for Job Instances"() { when: - final jobInstanceDao = new JcrJobInstanceDao(mockFactory) + final jobInstanceDao = new JcrGrabbitJobInstanceDao(mockFactory) final result = jobInstanceDao.jobNames then: result.containsAll(["someJob", "someOtherJob"]) } + + def "GetJobInstancePaths for jobExecutions"() { + when: + final jobInstanceDao = new JcrGrabbitJobInstanceDao(mockFactory) + final jobExecutionPaths = [ + "/var/grabbit/job/repository/jobExecutions/1", + "/var/grabbit/job/repository/jobExecutions/4" + ] + + final result = jobInstanceDao.getJobInstancePaths(jobExecutionPaths) + + then: + result != null + result.size() == 2 + } + } diff --git a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrStepExecutionDaoSpec.groovy b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitStepExecutionDaoSpec.groovy similarity index 76% rename from grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrStepExecutionDaoSpec.groovy rename to grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitStepExecutionDaoSpec.groovy index 03d12da..76cef19 100644 --- a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrStepExecutionDaoSpec.groovy +++ b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/JcrGrabbitStepExecutionDaoSpec.groovy @@ -22,21 +22,13 @@ import org.springframework.batch.core.BatchStatus import org.springframework.batch.core.JobExecution import org.springframework.batch.core.JobInstance import org.springframework.batch.core.JobParameters -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.Subject -import spock.lang.Unroll - -import static com.twcable.grabbit.spring.batch.repository.JcrStepExecutionDao.ID -import static com.twcable.grabbit.spring.batch.repository.JcrStepExecutionDao.JOB_EXECUTION_ID -import static com.twcable.grabbit.spring.batch.repository.JcrStepExecutionDao.NAME -import static com.twcable.grabbit.spring.batch.repository.JcrStepExecutionDao.STATUS -import static com.twcable.jackalope.JCRBuilder.node -import static com.twcable.jackalope.JCRBuilder.property -import static com.twcable.jackalope.JCRBuilder.repository - -@Subject(JcrStepExecutionDao) -class JcrStepExecutionDaoSpec extends Specification { +import spock.lang.* + +import static JcrGrabbitStepExecutionDao.* +import static com.twcable.jackalope.JCRBuilder.* + +@Subject(JcrGrabbitStepExecutionDao) +class JcrGrabbitStepExecutionDaoSpec extends Specification { @Shared ResourceResolverFactory mockFactory @@ -71,9 +63,9 @@ class JcrStepExecutionDaoSpec extends Specification { } - def "EnsureRootResource for JcrStepExecutionDao"() { + def "EnsureRootResource for JcrGrabbitStepExecutionDao"() { when: - final stepExecutionDao = new JcrStepExecutionDao(mockFactory) + final stepExecutionDao = new JcrGrabbitStepExecutionDao(mockFactory) stepExecutionDao.ensureRootResource() then: @@ -84,7 +76,7 @@ class JcrStepExecutionDaoSpec extends Specification { @Unroll def "GetStepExecution for a given JobExecution and a StepExecution id #stepExecutionId"() { when: - final stepExecutionDao = new JcrStepExecutionDao(mockFactory) + final stepExecutionDao = new JcrGrabbitStepExecutionDao(mockFactory) final result = stepExecutionDao.getStepExecution(new JobExecution(new JobInstance(1, "someJob"), jobExecutionId, new JobParameters()), stepExecutionId) then: @@ -98,4 +90,15 @@ class JcrStepExecutionDaoSpec extends Specification { 5 | 3 | BatchStatus.STARTED } + + @Ignore('TODO: Implement this test when Jackalope implements resourceResolver.findResources() API') + def "GetStepExecutionPaths for jobResourcePaths"() { + when: + final stepExecutionDao = new JcrGrabbitStepExecutionDao(mockFactory) + final result = stepExecutionDao.getStepExecutionPaths([]) + + then: + 1 == 1 + + } } diff --git a/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/servlets/GrabbitCleanJobRepositoryServletSpec.groovy b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/servlets/GrabbitCleanJobRepositoryServletSpec.groovy new file mode 100644 index 0000000..1342757 --- /dev/null +++ b/grabbit/src/test/groovy/com/twcable/grabbit/spring/batch/repository/servlets/GrabbitCleanJobRepositoryServletSpec.groovy @@ -0,0 +1,62 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.twcable.grabbit.spring.batch.repository.servlets + +import com.twcable.grabbit.spring.batch.repository.services.CleanJobRepository +import org.apache.sling.api.SlingHttpServletRequest +import org.apache.sling.api.SlingHttpServletResponse +import spock.lang.Specification +import spock.lang.Subject + +@Subject(GrabbitCleanJobRepositoryServlet) +class GrabbitCleanJobRepositoryServletSpec extends Specification { + + def "Servlet handles the case when hours parameter is not passed"() { + given: + def servlet = new GrabbitCleanJobRepositoryServlet(cleanJobRepository: Mock(CleanJobRepository)) + def request = Mock(SlingHttpServletRequest) + request.getParameter("hours") >> null + def response = Mock(SlingHttpServletResponse) + def writer = new StringWriter() + response.getWriter() >> new PrintWriter(writer) + when: + servlet.doPost(request, response) + + then: + writer != null + writer.toString() == "Parameter 'hours' must be an integer" + } + + def "Servlet handles the case when hours parameter is correctly passed"() { + given: + def clientJobRepository = Mock(CleanJobRepository) + clientJobRepository.cleanJobRepository(_) >> (["id1","id2","id3"] as List) + def servlet = new GrabbitCleanJobRepositoryServlet(cleanJobRepository: clientJobRepository) + def request = Mock(SlingHttpServletRequest) + request.getParameter("hours") >> 5 + def response = Mock(SlingHttpServletResponse) + def writer = new StringWriter() + response.getWriter() >> new PrintWriter(writer) + when: + servlet.doPost(request, response) + + then: + writer != null + writer.toString().contains("JobExecutionsIds that were removed") + writer.toString().contains("[id1, id2, id3]") + } +}