Async jobs mechanism migrated from VID code
Change-Id: Icf8bbac9ad83addcef2269839fb66f8c1ad31c5a
Signed-off-by: Aharoni, Pavel (pa0916) <pavel.aharoni@intl.att.com>
diff --git a/osam-core/async-jobs/pom.xml b/osam-core/async-jobs/pom.xml
new file mode 100644
index 0000000..1cc2ec6
--- /dev/null
+++ b/osam-core/async-jobs/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--/*-
+ * ============LICENSE_START=======================================================
+ * OSAM Core
+ * ================================================================================
+ * Copyright (C) 2018 Netsia
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>osam-core</artifactId>
+ <groupId>org.onap.osam</groupId>
+ <version>0.0.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>async-jobs</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-quartz</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.osam</groupId>
+ <artifactId>common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.osam</groupId>
+ <artifactId>model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/AsyncJobService.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/AsyncJobService.java
new file mode 100644
index 0000000..940bc5f
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/AsyncJobService.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+
+
+package org.onap.osam.job;
+
+import org.onap.osam.job.dao.job.JobStatus;
+
+import java.util.List;
+import java.util.UUID;
+
+public interface AsyncJobService {
+
+ JobStatus calcStatus(String asyncRequestStatus);
+
+ List<UUID> pushBulkJob(String userId, boolean isSuccessful, boolean isOLTDependant);
+
+/*
+ List<String> PARAMS_TO_IGNORE = Arrays.asList("vnf_name", "vf_module_name");
+
+ List<ServiceInfo> getAllServicesInfo();
+
+
+ String getServiceInstantiationPath(ServiceInstantiation serviceInstantiationRequest);
+
+ String getOrchestrationRequestsPath();
+
+ ServiceInfo getServiceInfoByJobId(UUID jobUUID);
+
+ List<JobAuditStatus> getAuditStatuses(UUID jobUUID, JobAuditStatus.SourceStatus source);
+
+ ServiceInfo updateServiceInfo(UUID jobUUID, Consumer<ServiceInfo> serviceUpdater);
+
+ ServiceInfo updateServiceInfoAndAuditStatus(UUID jobUuid, Job.JobStatus jobStatus);
+
+ void auditVidStatus(UUID jobUUID, Job.JobStatus jobStatus);
+
+ void auditMsoStatus(UUID jobUUID, AsyncRequestStatus.Request msoRequestStatus);
+
+ void auditMsoStatus(UUID jobUUID, String jobStatus, String requestId, String additionalInfo);
+
+ void handleFailedInstantiation(UUID jobUUID);
+
+ void deleteJob(UUID jobId);
+
+ void hideServiceInfo(UUID jobUUID);
+
+ int getCounterForName(String name);
+
+ int getMaxRetriesGettingFreeNameFromAai();
+
+ void setMaxRetriesGettingFreeNameFromAai(int maxRetriesGettingFreeNameFromAai);
+
+ String getUniqueName(String name, ResourceType resourceType);
+*/
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobCommand.java
new file mode 100644
index 0000000..ca594c0
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobCommand.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job;
+
+import org.onap.osam.job.impl.JobSharedData;
+
+import java.util.Map;
+
+
+/**
+ * A callable instance, with serializable characteristics.
+ * Represents a step in a chain of steps, which eventualy
+ * resides into a packing Job.
+ */
+public interface IJobCommand {
+
+ /**
+ * Initialize the command state
+ * @param sharedData shared data cross all job commands
+ * @param commandData An input to be set into the command. Each implementation may expect different keys in the map.
+ * @return Returns itself
+ */
+ default IJobCommand init(JobSharedData sharedData, Map<String, Object> commandData) {
+ return this;
+ }
+
+ /**
+ * @return Returns the inner state of the command. This state, once passed into init(), should
+ * bring the command back to it's state.
+ */
+ Map<String, Object> getData();
+
+ /**
+ * Execute the command represented by this instance. Assumes the instance is already init().
+ * @return A NextCommand containing the next command in chain of commands, or null if chain
+ * should be terminated. Might return itself (packed in a NextCommand).
+ */
+ NextCommand call();
+
+ default JobType getType() {
+ return JobType.jobTypeOf(this.getClass());
+ }
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobFactory.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobFactory.java
new file mode 100644
index 0000000..460aba2
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobFactory.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job;
+
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.impl.JobSharedData;
+
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * kind of factory for creating jobs and converting them to Job Model
+ */
+public interface IJobFactory {
+/*
+ JobModel toModel(Job job);
+*/
+
+ OsamJob createRootJob(JobType jobType, AsyncJobRequest request, String userId, Integer indexInBulk, Map<String, Object> jobData);
+
+ OsamJob createChildJob(JobType jobType, JobStatus jobStatus, AsyncJobRequest request, JobSharedData parentSharedData, Map<String, Object> jobData);
+
+ // Marks types that are an AsyncJob payload
+ interface AsyncJobRequest {
+ }
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobsDataAccessService.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobsDataAccessService.java
new file mode 100644
index 0000000..231c057
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/IJobsDataAccessService.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job;
+
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.UUID;
+
+public interface IJobsDataAccessService {
+
+ UUID add(OsamJob job);
+
+ Optional<OsamJob> pull(JobStatus topic, String ownerId);
+
+ void pushBack(OsamJob job);
+
+ Collection<OsamJob> peek();
+
+ OsamJob peek(UUID jobId);
+
+ void delete(UUID jobId);
+
+ boolean mute(UUID jobId);
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/JobType.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/JobType.java
new file mode 100644
index 0000000..1cabcad
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/JobType.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job;
+
+import org.onap.osam.job.command.HttpCallCommand;
+import org.onap.osam.job.command.NoOpCommand;
+import org.onap.osam.job.command.WatchingCommand;
+import org.onap.osam.job.command.demo.ChassisCommand;
+import org.onap.osam.job.command.demo.OLTCommand;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+
+public enum JobType {
+
+ HttpCall(HttpCallCommand.class),
+ Watching(WatchingCommand.class),
+ NoOp(NoOpCommand.class),
+ //Demo
+ ChassisCreation(ChassisCommand.class),
+ OLTCreation(OLTCommand.class);
+
+ private static final Map<Class, JobType> REVERSE_MAP = Stream.of(values())
+ .collect(Collectors.toMap(t -> t.getCommandClass(), t -> t));
+
+ private final Class commandClass;
+
+ <T extends IJobCommand> JobType(Class<T> commandClass) {
+ this.commandClass = commandClass;
+ }
+
+ public Class getCommandClass() {
+ return commandClass;
+ }
+ static JobType jobTypeOf(Class commandClass) {
+ return REVERSE_MAP.get(commandClass);
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/NextCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/NextCommand.java
new file mode 100644
index 0000000..6a61322
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/NextCommand.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job;
+
+import org.onap.osam.job.dao.job.JobStatus;
+
+public class NextCommand {
+ private final JobStatus status;
+ private final IJobCommand command;
+
+ public NextCommand(JobStatus nextStatus, IJobCommand nextCommand) {
+ this.status = nextStatus;
+ this.command = nextCommand;
+ }
+
+ public NextCommand(JobStatus nextStatus) {
+ this.status = nextStatus;
+ this.command = null;
+ }
+
+ public JobStatus getStatus() {
+ return status;
+ }
+
+ public IJobCommand getCommand() {
+ return command;
+ }
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/BaseInProgressStatusCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/BaseInProgressStatusCommand.java
new file mode 100644
index 0000000..e1de031
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/BaseInProgressStatusCommand.java
@@ -0,0 +1,142 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.IJobFactory;
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.IJobsDataAccessService;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.AsyncJobService;
+import org.onap.osam.job.impl.JobSharedData;
+import org.onap.osam.job.utils.TimeUtils;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeParseException;
+import java.util.Map;
+
+@Slf4j
+public abstract class BaseInProgressStatusCommand extends CommandBase implements IJobCommand {
+
+ protected AsyncJobService asyncInstantiationBL;
+
+ protected IJobsDataAccessService jobsDataAccessService;
+
+ protected IJobFactory jobAdapter;
+
+/*
+ @Inject
+ protected RestMsoImplementation restMso;
+
+ @Inject
+ protected AuditService auditService;
+*/
+
+ protected String requestId;
+
+ protected String instanceId;
+
+
+ @Override
+ public NextCommand call() {
+
+ // try {
+ String asyncRequestStatus = getAsyncRequestStatus();
+ //asyncInstantiationBL.auditMsoStatus(getSharedData().getRootJobId(), asyncRequestStatus.get().request);
+ JobStatus jobStatus = asyncInstantiationBL.calcStatus(asyncRequestStatus);
+ ZonedDateTime jobStartTime = getZonedDateTime(asyncRequestStatus);
+ jobStatus = isExpired(jobStartTime) ? JobStatus.FAILED : jobStatus;
+ return processJobStatus(jobStatus);
+
+ //TODO error handling
+ /* } catch (javax.ws.rs.ProcessingException e) {
+ // Retry when we can't connect MSO during getStatus
+ LOGGER.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, will retry: {}", requestId, e, e);
+ return new NextCommand(JobStatus.IN_PROGRESS, this);
+ } catch (BadResponseFromMso e) {
+ return handleFailedMsoResponse(e.getMsoResponse());
+ }
+ catch (RuntimeException e) {
+ LOGGER.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, stopping: {}", requestId, e, e);
+ return new NextCommand(JobStatus.STOPPED, this);
+ }*/
+ }
+
+ abstract NextCommand processJobStatus(JobStatus jobStatus);
+
+ abstract boolean isExpired(ZonedDateTime jobStartTime);
+
+ private String getAsyncRequestStatus() {
+/*
+ String path = asyncInstantiationBL.getOrchestrationRequestsPath()+"/"+requestId;
+ RestObject<AsyncRequestStatus> msoResponse = restMso.GetForObject(path, AsyncRequestStatus.class);
+ if (msoResponse.getStatusCode() >= 400 || msoResponse.get() == null) {
+ throw new BadResponseFromMso(msoResponse);
+ }
+*/
+ //TODO
+ return "dummy";
+ }
+
+ /*private NextCommand handleFailedMsoResponse(RestObject<AsyncRequestStatus> msoResponse) {
+ auditService.setFailedAuditStatusFromMso(getSharedData().getJobUuid(), requestId, msoResponse.getStatusCode(), msoResponse.getRaw());
+ LOGGER.error(EELFLoggerDelegate.errorLogger,
+ "Failed to get orchestration status for {}. Status code: {}, Body: {}",
+ requestId, msoResponse.getStatusCode(), msoResponse.getRaw());
+ return new NextCommand(JobStatus.IN_PROGRESS, this);
+ }*/
+
+ @Override
+ public BaseInProgressStatusCommand init(JobSharedData sharedData, Map<String, Object> commandData) {
+ return init(sharedData, (String) commandData.get("requestId"), (String) commandData.get("instanceId"));
+ }
+
+
+ protected BaseInProgressStatusCommand init(JobSharedData sharedData,
+ String requestId,
+ String instanceId) {
+ init(sharedData);
+ this.requestId = requestId;
+ this.instanceId = instanceId;
+ return this;
+ }
+
+ @Override
+ public Map<String, Object> getData() {
+ return ImmutableMap.of(
+ "requestId", requestId,
+ "instanceId", instanceId
+ );
+ }
+
+ private ZonedDateTime getZonedDateTime(String response) {
+ ZonedDateTime jobStartTime;
+ try {
+ //TODO dummy time until real impl is provided
+ jobStartTime = TimeUtils.parseZonedDateTime(ZonedDateTime.now().toString());
+ } catch (DateTimeParseException | NullPointerException e) {
+ log.error("Failed to parse start time for {}, body: {}. Current time will be used", requestId, response, e);
+ jobStartTime = ZonedDateTime.now();
+ }
+ return jobStartTime;
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/BaseWatchingCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/BaseWatchingCommand.java
new file mode 100644
index 0000000..711c27d
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/BaseWatchingCommand.java
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import lombok.extern.slf4j.Slf4j;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.AsyncJobService;
+import org.onap.osam.job.impl.JobSharedData;
+import org.onap.osam.job.repository.job.OsamJobRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+@Slf4j
+public abstract class BaseWatchingCommand extends CommandBase implements IJobCommand {
+
+ @Autowired
+ protected AsyncJobService asyncInstantiationBL;
+
+ @Autowired
+ private OsamJobRepository osamJobRepository;
+
+ private List<UUID> childrenJobsIds;
+
+ protected boolean isRoot;
+
+ public BaseWatchingCommand() {}
+
+ public BaseWatchingCommand(JobSharedData sharedData, List<UUID> childrenJobsIds, boolean isRoot) {
+ init(sharedData, childrenJobsIds, isRoot);
+ }
+
+ @Override
+ public BaseWatchingCommand init(JobSharedData sharedData, Map<String, Object> commandData) {
+ return init(
+ sharedData,
+ ((List<String>) commandData.get("childrenJobs")).stream().map(x -> UUID.fromString(x)).collect(Collectors.toList()),
+ (boolean) commandData.get("isRoot")
+ );
+ }
+
+ protected BaseWatchingCommand init(JobSharedData sharedData, List<UUID> childrenJobsIds, boolean isRoot) {
+ super.init(sharedData);
+ this.childrenJobsIds = CollectionUtils.isEmpty(childrenJobsIds) ? new ArrayList<>() : childrenJobsIds;
+ this.isRoot = isRoot;
+ return this;
+ }
+
+ @Override
+ public NextCommand call() {
+ Map<UUID, OsamJob> jobs = getAllChildrenJobs();
+
+ boolean isAllChildrenFinal = true;
+ boolean hasFailedChild = false;
+ for (UUID jobId: childrenJobsIds) {
+ OsamJob job = jobs.get(jobId);
+
+
+ //if job not found - we assume it failed
+ if (job == null || job.getStatus() == null) {
+ hasFailedChild = true;
+ continue;
+ }
+
+ if (!job.getStatus().isFinal()) {
+ isAllChildrenFinal = false;
+ } else if (!job.getStatus().equals(JobStatus.COMPLETED)) {
+ //if job status is final - check if it failed status
+ hasFailedChild = true;
+ }
+ }
+
+ return getNextCommand(isAllChildrenFinal, hasFailedChild);
+ }
+
+ private Map<UUID, OsamJob> getAllChildrenJobs() {
+ List<OsamJob> jobs = osamJobRepository.findAllByUuid(childrenJobsIds);
+ return jobs.stream().collect(Collectors.toMap(OsamJob::getUuid, item -> item));
+ }
+
+ protected abstract NextCommand getNextCommand(boolean isAllChildrenFinal, boolean hasFailedChild);
+
+
+ @Override
+ public Map<String, Object> getData() {
+ Map<String, Object> data = new HashMap<>();
+ data.put("childrenJobs", childrenJobsIds);
+ data.put("isRoot", isRoot);
+ return data;
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/CommandBase.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/CommandBase.java
new file mode 100644
index 0000000..94c43e7
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/CommandBase.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.osam.job.impl.JobSharedData;
+@Slf4j
+@Getter
+@Setter
+public abstract class CommandBase {
+
+ private JobSharedData sharedData;
+
+ protected CommandBase init(JobSharedData sharedData) {
+ this.setSharedData(sharedData);
+ return this;
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/HttpCallCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/HttpCallCommand.java
new file mode 100644
index 0000000..372d751
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/HttpCallCommand.java
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import com.google.common.collect.ImmutableMap;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.impl.JobSharedData;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.http.HttpEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Map;
+import java.util.UUID;
+
+
+@Component
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+public class HttpCallCommand implements IJobCommand {
+ private String url;
+ private UUID uuid;
+
+ public HttpCallCommand() {
+ }
+
+ public HttpCallCommand(String url, UUID uuid) {
+ init(url, uuid);
+ }
+
+ @Override
+ public NextCommand call() {
+ RestTemplate restTemplate = new RestTemplate();
+ HttpEntity<String> request = new HttpEntity<>(new String(uuid.toString()));
+ String str = restTemplate.postForObject(url, request, String.class);
+ return new NextCommand(JobStatus.COMPLETED);
+ }
+
+ @Override
+ public HttpCallCommand init(JobSharedData sharedData, Map<String, Object> commandData) {
+ return init((String) commandData.get("url"), sharedData.getJobUuid());
+ }
+
+ private HttpCallCommand init(String url, UUID jobUuid) {
+ this.url = url;
+ this.uuid = jobUuid;
+ return this;
+ }
+
+ @Override
+ public Map<String, Object> getData() {
+ return ImmutableMap.of("url", url);
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/JobCommandFactory.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/JobCommandFactory.java
new file mode 100644
index 0000000..dc92182
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/JobCommandFactory.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import org.onap.osam.common.exception.GenericUncheckedException;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobCommand;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
+
+import java.util.function.Function;
+
+@Component
+public class JobCommandFactory {
+
+ final Function<Class<? extends IJobCommand>, IJobCommand> jobFactory;
+
+ @Autowired
+ public JobCommandFactory(ApplicationContext applicationContext) {
+ this.jobFactory = (jobType -> {
+ final Object commandBean = applicationContext.getBean(jobType);
+
+ if (!(commandBean instanceof IJobCommand)) {
+ throw new GenericUncheckedException(commandBean.getClass() + " is not a IJobCommand");
+ }
+
+ return (IJobCommand) commandBean;
+ });
+ }
+
+ public JobCommandFactory(Function<Class<? extends IJobCommand>, IJobCommand> jobFactory) {
+ this.jobFactory = jobFactory;
+ }
+
+ public IJobCommand toCommand(OsamJob job) {
+
+ final IJobCommand command = jobFactory.apply(job.getType().getCommandClass());
+ command.init(job.getSharedData(), job.getDataMap());
+
+ return command;
+ }
+
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/NoOpCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/NoOpCommand.java
new file mode 100644
index 0000000..8f110a1
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/NoOpCommand.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.NextCommand;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.Map;
+
+@Component
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+public class NoOpCommand implements IJobCommand {
+
+ @Override
+ public NextCommand call() {
+ return null;
+ }
+
+ @Override
+ public Map<String, Object> getData() {
+ return Collections.emptyMap();
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/WatchingCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/WatchingCommand.java
new file mode 100644
index 0000000..70b9f00
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/WatchingCommand.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.impl.JobSharedData;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.UUID;
+
+@Component
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+public class WatchingCommand extends BaseWatchingCommand {
+
+ public WatchingCommand() {}
+
+ public WatchingCommand(JobSharedData sharedData, List<UUID> childrenJobsIds, boolean isRoot) {
+ super(sharedData, childrenJobsIds, isRoot);
+ }
+
+ protected NextCommand getNextCommand(boolean isAllChildrenFinal, boolean hasFailedChild) {
+ if (isAllChildrenFinal) {
+ JobStatus jobStatus = hasFailedChild ? JobStatus.COMPLETED_WITH_ERRORS : JobStatus.COMPLETED;
+ return new NextCommand(jobStatus);
+ } else {
+ if (isRoot) {
+ return new NextCommand(JobStatus.IN_PROGRESS, this);
+ }
+ return new NextCommand(JobStatus.RESOURCE_IN_PROGRESS, this);
+ }
+ }
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/demo/ChassisCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/demo/ChassisCommand.java
new file mode 100644
index 0000000..98de930
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/demo/ChassisCommand.java
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command.demo;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.osam.job.command.WatchingCommand;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.JobType;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.command.CommandBase;
+import org.onap.osam.job.impl.DummyAsyncRequest;
+import org.onap.osam.job.impl.JobFactory;
+import org.onap.osam.job.impl.JobSharedData;
+import org.onap.osam.job.impl.JobsDataAccessService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+@Slf4j
+@Component
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+
+/**
+ * In this example, Chassis job is successful and spawns off OLT job - without being dependent on it.
+ */
+
+public class ChassisCommand extends CommandBase implements IJobCommand {
+
+ private Boolean isSuccessful;
+
+ private Boolean isOltDependant;
+
+ @Autowired
+ protected JobsDataAccessService jobsDataAccessService;
+
+ @Autowired
+ protected JobFactory jobFactory;
+
+ public ChassisCommand(){}
+
+ @Override
+ public IJobCommand init(JobSharedData sharedData, Map<String, Object> commandData) {
+ super.init(sharedData);
+ isSuccessful = (Boolean) commandData.get("isSuccessful");
+ isOltDependant = (Boolean) commandData.get("isOltDependant");
+ return this;
+ }
+
+ @Override
+ public Map<String, Object> getData() {
+ return ImmutableMap.of("isSuccessful", isSuccessful,
+ "isOltDependant", isOltDependant);
+ }
+
+ @Override
+ public NextCommand call() {
+ NextCommand nextCommand;
+ if (isSuccessful){
+ log.debug("ChassisCommand - it's your LUCKY day! :) ChassisCreation created, continuing to OLTCreation...");
+
+ //Adding an OLTCreation child job
+ final List<UUID> oltChildJobs = getOltChildJobs();
+
+ if (isOltDependant){
+ log.debug("ChassisCommand - OLT Dependent scenario. Pending to wait if OLT job succeeds before deciding if Chassis job succeeds.");
+ nextCommand = new NextCommand(JobStatus.PENDING, new WatchingCommand(getSharedData(), oltChildJobs, true));
+ } else {
+ log.debug("ChassisCommand - independent scenario. This job is completed, regardless of child OLT job.");
+ nextCommand = new NextCommand(JobStatus.COMPLETED);
+ }
+ } else {
+ log.debug("ChassisCommand - it's your UNLUCKY day! :( ChassisCreation creation failed, your bulk request is finished here.");
+ nextCommand = new NextCommand(JobStatus.FAILED);
+ }
+ return nextCommand;
+ }
+
+ private List<UUID> getOltChildJobs() {
+ log.debug("Spinning off OLT child job....");
+ Map<String, Object> dataForOLTChild = ImmutableMap.of();
+ return Lists.newArrayList(jobsDataAccessService.add(jobFactory.createChildJob(JobType.OLTCreation, JobStatus.CREATING, new DummyAsyncRequest(), getSharedData(), ImmutableMap.of())));
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/demo/OLTCommand.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/demo/OLTCommand.java
new file mode 100644
index 0000000..4de73ad
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/command/demo/OLTCommand.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command.demo;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.command.CommandBase;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Slf4j
+@Component
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+public class OLTCommand extends CommandBase implements IJobCommand {
+ @Override
+ public Map<String, Object> getData() {
+ return ImmutableMap.of();
+ }
+
+ @Override
+ public NextCommand call() {
+ log.debug("OLTCreation Command - for this demo, I'm always successful!!");
+ return new NextCommand(JobStatus.COMPLETED);
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/dao/job/JobStatus.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/dao/job/JobStatus.java
new file mode 100644
index 0000000..c3b031f
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/dao/job/JobStatus.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.dao.job;
+
+public enum JobStatus {
+ COMPLETED(true),
+ FAILED(true),
+ IN_PROGRESS(false),
+ RESOURCE_IN_PROGRESS(false),
+ PAUSE(false),
+ PENDING(false),
+ STOPPED(true),
+ COMPLETED_WITH_ERRORS(true),
+ CREATING(false);
+
+ private final Boolean finalStatus;
+ public Boolean isFinal(){return finalStatus;}
+
+ JobStatus(Boolean finalStatus)
+ {
+ this.finalStatus = finalStatus ;
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/dao/job/OsamJob.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/dao/job/OsamJob.java
new file mode 100644
index 0000000..807221f
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/dao/job/OsamJob.java
@@ -0,0 +1,181 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.dao.job;
+
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.MoreObjects;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.hibernate.annotations.DynamicUpdate;
+import org.hibernate.annotations.SelectBeforeUpdate;
+import org.hibernate.annotations.Type;
+import org.onap.osam.job.JobType;
+import org.onap.osam.job.impl.JobData;
+import org.onap.osam.job.impl.JobSharedData;
+import org.onap.osam.model.dao.BaseEntity;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Column;
+import javax.persistence.Converter;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Lob;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+/*
+ The following 2 annotations let hibernate to update only fields that actually have been changed.
+ DynamicUpdate tell hibernate to update only dirty fields.
+ SelectBeforeUpdate is needed since during update the entity is detached (get and update are in different sessions)
+*/
+@DynamicUpdate()
+@SelectBeforeUpdate()
+@Entity
+@Getter
+@Setter
+@Slf4j
+public class OsamJob extends BaseEntity {
+
+ @Column(unique = true, nullable = false, columnDefinition = "CHAR(36)")
+ @Type(type="org.hibernate.type.UUIDCharType")
+ private UUID uuid;
+
+ @CreatedDate
+ private Date createdDate;
+
+ @LastModifiedDate
+ private Date modifiedDate;
+
+ @Enumerated(value = EnumType.STRING)
+ private JobStatus status;
+
+ @Enumerated(value = EnumType.STRING)
+ private JobType type;
+
+ @Lob
+ @Column
+ private JobData data = new JobData();
+
+ @Column
+ private String takenBy;
+
+ @Column
+ private String userId;
+
+ @Column(nullable = false)
+ private Integer age = 0;
+
+ @Column(nullable = false)
+ private Integer indexInBulk = 0;
+
+ @Column
+ private Date deletedAt;
+
+ public Map<String, Object> getDataMap() {
+ return data.getCommandData().get(getType());
+ }
+
+ public void setTypeAndData(JobType jobType, Map<String, Object> data) {
+ // *add* the data to map,
+ // then change state to given type
+ this.type = jobType;
+ this.data.getCommandData().put(jobType, data);
+ }
+
+ public void setSharedData(JobSharedData sharedData) {
+ this.data.setSharedData(sharedData);
+ }
+
+ public JobSharedData getSharedData(){
+ return this.data.getSharedData();
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof OsamJob)) return false;
+ OsamJob daoJob = (OsamJob) o;
+ return Objects.equals(getUuid(), daoJob.getUuid());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getUuid());
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("status", status)
+ .add("type", type)
+ .add("uuid", uuid)
+ .add("takenBy", takenBy)
+ .add("userId", userId)
+ .add("age", age)
+ .add("createdDate", createdDate)
+ .add("modifiedDate", modifiedDate)
+ .add("deletedAt", deletedAt)
+ .add("data", data)
+ .toString();
+ }
+
+ @Converter(autoApply = true)
+ public static class JobDataConverter implements AttributeConverter<JobData, String> {
+
+ @Override
+ public String convertToDatabaseColumn(JobData jobData) {
+ if( jobData == null )
+ return null;
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ try {
+ return mapper.writeValueAsString(jobData);
+ } catch (JsonProcessingException e) {
+ log.error("Couldn't persist JobData object {}, error: {}. Persisting null", jobData, e);
+ return null;
+ }
+ }
+
+ @Override
+ public JobData convertToEntityAttribute(String s) {
+ if( s == null )
+ return null;
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ try {
+ return mapper.readValue(s, JobData.class);
+ } catch (IOException e) {
+ log.error("Couldn't deserialize {} to JobData object, error: {}. Returning null", s, e);
+ return null;
+ }
+ }
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/exceptions/JobException.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/exceptions/JobException.java
new file mode 100644
index 0000000..46ba80e
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/exceptions/JobException.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.exceptions;
+
+import java.util.UUID;
+
+public class JobException extends RuntimeException {
+ private final UUID jobUuid;
+
+ public JobException(String message, UUID jobUuid, Throwable cause) {
+ super(message, cause);
+ this.jobUuid = jobUuid;
+ }
+
+ public UUID getJobUuid() {
+ return jobUuid;
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/AsyncJobServiceImpl.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/AsyncJobServiceImpl.java
new file mode 100644
index 0000000..c964f01
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/AsyncJobServiceImpl.java
@@ -0,0 +1,506 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+
+
+package org.onap.osam.job.impl;
+
+import com.google.common.collect.ImmutableMap;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.AsyncJobService;
+import org.onap.osam.job.JobType;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+@Service
+public class AsyncJobServiceImpl implements AsyncJobService {
+
+ private final JobFactory jobFactory;
+
+ private final JobsDataAccessService jobsDataAccessService;
+
+ private Map<String, JobStatus> msoStateToJobStatusMap = ImmutableMap.<String, JobStatus>builder()
+ .put("inprogress", JobStatus.IN_PROGRESS)
+ .put("failed", JobStatus.FAILED)
+ .put("pause", JobStatus.PAUSE)
+ .put("paused", JobStatus.PAUSE)
+ .put("complete", JobStatus.COMPLETED)
+ .put("pending", JobStatus.IN_PROGRESS)
+ .put("pendingmanualtask", JobStatus.PAUSE)
+ .put("unlocked", JobStatus.IN_PROGRESS)
+ .build();
+
+
+ public AsyncJobServiceImpl(JobFactory jobFactory, JobsDataAccessService jobsDataAccessService) {
+ this.jobFactory = jobFactory;
+ this.jobsDataAccessService = jobsDataAccessService;
+ }
+
+ @Override
+ public JobStatus calcStatus(String asyncRequestStatus) {
+ JobStatus jobStatus = msoStateToJobStatusMap.get(asyncRequestStatus);
+ return (jobStatus != null ? jobStatus : JobStatus.IN_PROGRESS);
+ }
+
+ @Override
+ public List<UUID> pushBulkJob(String userId, boolean isSuccessful, boolean isOltDependant) {
+ List<UUID> uuids = new ArrayList<>();
+ Date createdBulkDate = Calendar.getInstance().getTime();
+ int bulkSize = 1;
+ for (int i = 0; i < bulkSize; i++) {
+ OsamJob job = jobFactory.createRootJob(JobType.ChassisCreation, new DummyAsyncRequest(), userId, i,
+ ImmutableMap.of("isSuccessful", isSuccessful, "isOltDependant", isOltDependant));
+ UUID jobId = jobsDataAccessService.add(job);
+ uuids.add(jobId);
+ }
+ return uuids;
+ }
+
+
+
+/*
+ private final DataAccessService dataAccessService;
+
+ private final IJobFactory jobAdapter;
+
+ private final IJobsDataAccessService jobService;
+
+ private SessionFactory sessionFactory;
+
+ private AaiClientInterface aaiClient;
+
+ private int maxRetriesGettingFreeNameFromAai = MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI;
+
+ private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AsyncInstantiationBusinessLogicImpl.class);
+
+
+
+ @Autowired
+ public AsyncInstantiationBusinessLogicImpl(DataAccessService dataAccessService,
+ IJobFactory jobAdapter,
+ IJobsDataAccessService jobService,
+ SessionFactory sessionFactory,
+ AaiClientInterface aaiClient) {
+ this.dataAccessService = dataAccessService;
+ this.jobAdapter = jobAdapter;
+ this.jobService = jobService;
+ this.sessionFactory = sessionFactory;
+ this.aaiClient = aaiClient;
+ }
+
+ @Override
+ public List<ServiceInfo> getAllServicesInfo() {
+ return dataAccessService.getList(ServiceInfo.class, filterByCreationDateAndNotDeleted(), orderByCreatedDateAndStatus(), null);
+ }
+
+ private String filterByCreationDateAndNotDeleted() {
+ LocalDateTime minus3Months = LocalDateTime.now().minusMonths(3);
+ Timestamp filterDate = Timestamp.valueOf(minus3Months);
+ return " where" +
+ " hidden = false" +
+ " and deleted_at is null" + // don't fetch deleted
+ " and created >= '" + filterDate + "' ";
+ }
+
+ private String orderByCreatedDateAndStatus() {
+ return " createdBulkDate DESC ,\n" +
+ " (CASE jobStatus\n" +
+ " WHEN 'COMPLETED' THEN 0\n" +
+ " WHEN 'FAILED' THEN 0\n" +
+ " WHEN 'IN_PROGRESS' THEN 1\n" +
+ " WHEN 'PAUSE' THEN 2\n" +
+ " WHEN 'PENDING' THEN 3\n" +
+ " WHEN 'STOPPED' THEN 3 END),\n" +
+ " statusModifiedDate ";
+ }
+
+ @Override
+ public List<UUID> pushBulkJob(ServiceInstantiation request, String userId) {
+ List<UUID> uuids = new ArrayList<>();
+ Date createdBulkDate = Calendar.getInstance().getTime();
+ int bulkSize = request.getBulkSize();
+ UUID templateId = UUID.randomUUID();
+ for (int i = 0; i < bulkSize; i++) {
+ //Job job = jobAdapter.createJob(JobType.ServiceInstantiation, request, templateId, userId, i);
+ Job job = jobAdapter.createJob(JobType.NoOp, request, templateId, userId, i);//should be some instatiation, this was changed as part of code cleaning
+
+ UUID jobId = jobService.add(job);
+ auditVidStatus(jobId,getStatus());
+ uuids.add(jobId);
+ dataAccessService.saveDomainObject(createServiceInfo(userId, request, jobId, templateId, createdBulkDate), DaoUtils.getPropsMap());
+ }
+ return uuids;
+ }
+
+ private ServiceInfo createServiceInfo(String userId, ServiceInstantiation serviceInstantiation, UUID jobId, UUID templateId, Date createdBulkDate) {
+ return new ServiceInfo(
+ userId, JobStatus.PENDING, serviceInstantiation.isPause(), jobId, templateId,
+ serviceInstantiation.getOwningEntityId(),
+ serviceInstantiation.getOwningEntityName(),
+ serviceInstantiation.getProjectName(),
+ serviceInstantiation.getAicZoneId(),
+ serviceInstantiation.getAicZoneName(),
+ serviceInstantiation.getTenantId(),
+ serviceInstantiation.getTenantName(),
+ serviceInstantiation.getLcpCloudRegionId(),
+ null,
+ serviceInstantiation.getSubscriptionServiceType(),
+ serviceInstantiation.getSubscriberName(),
+ null,
+ serviceInstantiation.getInstanceName(),
+ serviceInstantiation.getModelInfo().getModelInvariantId(),
+ serviceInstantiation.getModelInfo().getModelName(),
+ serviceInstantiation.getModelInfo().getModelVersion(),
+ createdBulkDate
+ );
+ }
+
+
+ */
+/*//*
+/@Override
+ public RequestDetailsWrapper<ServiceInstantiationRequestDetails> generateServiceInstantiationRequest(UUID jobId, ServiceInstantiation payload, String userId) {
+
+ ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity owningEntity = new ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity(payload.getOwningEntityId(), payload.getOwningEntityName());
+
+ SubscriberInfo subscriberInfo = new SubscriberInfo();
+ subscriberInfo.setGlobalSubscriberId(payload.getGlobalSubscriberId());
+
+ String serviceInstanceName = null;
+ if(payload.isUserProvidedNaming()) {
+ serviceInstanceName = getUniqueName(payload.getInstanceName(), ResourceType.SERVICE_INSTANCE);
+ String finalServiceInstanceName = serviceInstanceName;
+ updateServiceInfo(jobId, x -> x.setServiceInstanceName(finalServiceInstanceName));
+ }
+ ServiceInstantiationRequestDetails.RequestInfo requestInfo = new ServiceInstantiationRequestDetails.RequestInfo(
+ serviceInstanceName,
+ payload.getProductFamilyId(),
+ "VID",
+ payload.isRollbackOnFailure(),
+ userId);
+
+ List<ServiceInstantiationRequestDetails.ServiceInstantiationService> serviceInstantiationService = new LinkedList<>();
+ List<Map<String, String>> unFilteredInstanceParams = payload.getInstanceParams() != null ? payload.getInstanceParams() : new LinkedList<>();
+ List<Map<String, String>> filteredInstanceParams = removeUnNeededParams(unFilteredInstanceParams);
+ ServiceInstantiationRequestDetails.ServiceInstantiationService serviceInstantiationService1 = new ServiceInstantiationRequestDetails.ServiceInstantiationService(
+ payload.getModelInfo(),
+ serviceInstanceName,
+ filteredInstanceParams,
+ createServiceInstantiationVnfList(payload)
+ );
+ serviceInstantiationService.add(serviceInstantiationService1);
+
+ ServiceInstantiationRequestDetails.RequestParameters requestParameters = new ServiceInstantiationRequestDetails.RequestParameters(payload.getSubscriptionServiceType(), false, serviceInstantiationService);
+
+ ServiceInstantiationRequestDetails.Project project = payload.getProjectName() != null ? new ServiceInstantiationRequestDetails.Project(payload.getProjectName()) : null;
+
+ ServiceInstantiationRequestDetails requestDetails = new ServiceInstantiationRequestDetails(payload.getModelInfo(), owningEntity, subscriberInfo,
+ project, requestInfo, requestParameters);
+
+ RequestDetailsWrapper<ServiceInstantiationRequestDetails> requestDetailsWrapper = new RequestDetailsWrapper(requestDetails);
+ debugRequestDetails(requestDetailsWrapper, logger);
+ return requestDetailsWrapper;
+ }*//*
+
+
+ private List<Map<String, String>> removeUnNeededParams(List<Map<String, String>> instanceParams) {
+ List<String> keysToRemove = new ArrayList<>();
+ if (instanceParams != null && !instanceParams.isEmpty()) {
+ for (String key : instanceParams.get(0).keySet()) {
+ for (String paramToIgnore : PARAMS_TO_IGNORE)
+ if ((key.equalsIgnoreCase(paramToIgnore))) {
+ keysToRemove.add(key);
+ }
+ }
+ for (String key : keysToRemove) {
+ instanceParams.get(0).remove(key);
+ }
+ //TODO will be removed on once we stop using List<Map<String, String>>
+ if (instanceParams.get(0).isEmpty()) {
+ return Collections.emptyList();
+ }
+ }
+ return instanceParams;
+ }
+
+ private ServiceInstantiationRequestDetails.ServiceInstantiationVnfList createServiceInstantiationVnfList(ServiceInstantiation payload) {
+ CloudConfiguration cloudConfiguration = new CloudConfiguration();
+ cloudConfiguration.setTenantId(payload.getTenantId());
+ cloudConfiguration.setLcpCloudRegionId(payload.getLcpCloudRegionId());
+
+ Map<String, Vnf> vnfs = payload.getVnfs();
+ List<ServiceInstantiationRequestDetails.ServiceInstantiationVnf> vnfList = new ArrayList<>();
+ for (Vnf vnf : vnfs.values()) {
+ Map<String, Map<String, VfModule>> vfModules = vnf.getVfModules();
+ List<VfModule> convertedUnFilteredVfModules = convertVfModuleMapToList(vfModules);
+ List<VfModule> filteredVfModules = filterInstanceParamsFromVfModuleAndUniqueNames(convertedUnFilteredVfModules, vnf.isUserProvidedNaming());
+ ServiceInstantiationRequestDetails.ServiceInstantiationVnf serviceInstantiationVnf = new ServiceInstantiationRequestDetails.ServiceInstantiationVnf(
+ vnf.getModelInfo(),
+ cloudConfiguration,
+ vnf.getPlatformName(),
+ vnf.getLineOfBusiness(),
+ payload.getProductFamilyId(),
+ removeUnNeededParams(vnf.getInstanceParams()),
+ filteredVfModules,
+ vnf.isUserProvidedNaming() ? getUniqueName(vnf.getInstanceName(), ResourceType.GENERIC_VNF) : null
+ );
+ vnfList.add(serviceInstantiationVnf);
+ }
+
+ return new ServiceInstantiationRequestDetails.ServiceInstantiationVnfList(vnfList);
+ }
+
+ private List<VfModule> convertVfModuleMapToList(Map<String, Map<String, VfModule>> vfModules) {
+ return vfModules.values().stream().flatMap(vfModule -> vfModule.values().stream()).collect(Collectors.toList());
+ }
+
+ private List<VfModule> filterInstanceParamsFromVfModuleAndUniqueNames(List<VfModule> unFilteredVfModules, boolean isUserProvidedNaming) {
+ return unFilteredVfModules.stream().map(vfModule ->
+ new VfModule(
+ vfModule.getModelInfo(),
+ getUniqueNameIfNeeded(isUserProvidedNaming, vfModule.getInstanceName(), ResourceType.VF_MODULE),
+ getUniqueNameIfNeeded(isUserProvidedNaming, vfModule.getVolumeGroupInstanceName(), ResourceType.VOLUME_GROUP),
+ removeUnNeededParams(vfModule.getInstanceParams())))
+ .collect(Collectors.toList());
+ }
+
+ private String getUniqueNameIfNeeded(boolean isUserProvidedNaming, String name, ResourceType resourceType) {
+ return isUserProvidedNaming && !StringUtils.isEmpty(name) ?
+ getUniqueName(name, resourceType) : null;
+ }
+
+ @Override
+ public String getServiceInstantiationPath(ServiceInstantiation serviceInstantiationRequest) {
+ //in case pause flag is true - use assign , else - use create.
+ return MsoBusinessLogicImpl.validateEndpointPath(
+ serviceInstantiationRequest.isPause() ?
+ "mso.restapi.serviceInstanceAssign" : "mso.restapi.serviceInstanceCreate"
+ );
+ }
+
+ @Override
+ public String getOrchestrationRequestsPath() {
+ return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_GET_ORC_REQ);
+ }
+
+ @Override
+ public ServiceInfo updateServiceInfo(UUID jobUUID, Consumer<ServiceInfo> serviceUpdater) {
+ ServiceInfo serviceInfo = getServiceInfoByJobId(jobUUID);
+ serviceUpdater.accept(serviceInfo);
+ dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
+ return serviceInfo;
+ }
+
+
+
+ private void setServiceInfoStatus(ServiceInfo serviceInfo, JobStatus jobStatus) {
+ serviceInfo.setJobStatus(jobStatus);
+ serviceInfo.setStatusModifiedDate(new Date());
+ }
+
+ public ServiceInfo getServiceInfoByJobId(UUID jobUUID) {
+ List<ServiceInfo> serviceInfoList = dataAccessService.getList(ServiceInfo.class, String.format(" where jobId = '%s' ", jobUUID), null, null);
+ if (serviceInfoList.size() != 1) {
+ throw new GenericUncheckedException("Failed to retrieve job with uuid " + jobUUID + " from ServiceInfo table. Instances found: " + serviceInfoList.size());
+ }
+ return serviceInfoList.get(0);
+ }
+
+ public List<JobAuditStatus> getAuditStatuses(UUID jobUUID, JobAuditStatus.SourceStatus source) {
+ return dataAccessService.getList(
+ JobAuditStatus.class,
+ String.format(" where SOURCE = '%s' and JOB_ID = '%s'",source, jobUUID),
+ " CREATED_DATE ", null);
+ }
+
+ private JobAuditStatus getLatestAuditStatus(UUID jobUUID, JobAuditStatus.SourceStatus source){
+ List<JobAuditStatus> list = getAuditStatuses(jobUUID,source);
+ return !list.isEmpty() ? list.get(list.size()-1) : null;
+ }
+
+ @Override
+ public void auditVidStatus(UUID jobUUID, JobStatus jobStatus){
+ JobAuditStatus vidStatus = new JobAuditStatus(jobUUID, jobStatus.toString(), JobAuditStatus.SourceStatus.VID);
+ auditStatus(vidStatus);
+ }
+
+ @Override
+ public void auditMsoStatus(UUID jobUUID, AsyncRequestStatus.Request msoRequestStatus){
+ auditMsoStatus(jobUUID, msoRequestStatus.requestStatus.getRequestState(), msoRequestStatus.requestId, msoRequestStatus.requestStatus.getStatusMessage());
+ }
+
+ @Override
+ public void auditMsoStatus(UUID jobUUID, String jobStatus, String requestId, String additionalInfo){
+ JobAuditStatus msoStatus = new JobAuditStatus(jobUUID, jobStatus, JobAuditStatus.SourceStatus.MSO,
+ requestId != null ? UUID.fromString(requestId) : null,
+ additionalInfo);
+ auditStatus(msoStatus);
+ }
+
+ private void auditStatus(JobAuditStatus jobAuditStatus){
+ JobAuditStatus latestStatus = getLatestAuditStatus(jobAuditStatus.getJobId(),jobAuditStatus.getSource());
+ if (latestStatus == null || !latestStatus.equals(jobAuditStatus))
+ dataAccessService.saveDomainObject(jobAuditStatus, DaoUtils.getPropsMap());
+
+ }
+
+
+
+ @Override
+ public void handleFailedInstantiation(UUID jobUUID) {
+ ServiceInfo serviceInfo = updateServiceInfoAndAuditStatus(jobUUID, JobStatus.FAILED);
+ List<ServiceInfo> serviceInfoList = dataAccessService.getList(
+ ServiceInfo.class,
+ String.format(" where templateId = '%s' and jobStatus = '%s'",
+ serviceInfo.getTemplateId(),
+ JobStatus.PENDING),
+ null, null);
+ serviceInfoList.forEach(si -> updateServiceInfoAndAuditStatus(si.getJobId(), JobStatus.STOPPED));
+ }
+
+ @Override
+ public void deleteJob(UUID jobId) {
+ jobService.delete(jobId);
+ Date now = new Date();
+ updateServiceInfo(jobId, x -> x.setDeletedAt(now));
+ }
+
+ @Override
+ public void hideServiceInfo(UUID jobUUID) {
+ ServiceInfo serviceInfo = getServiceInfoByJobId(jobUUID);
+ if (!serviceInfo.getJobStatus().isFinal()) {
+ String message = String.format( "jobId %s: Service status does not allow hide service, status = %s",
+ serviceInfo.getJobId(),
+ serviceInfo.getJobStatus());
+ logger.error(EELFLoggerDelegate.errorLogger, message);
+ throw new OperationNotAllowedException(message);
+ }
+ serviceInfo.setHidden(true);
+ dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
+ }
+
+ @Override
+ public int
+
+
+ getCounterForName(String name) {
+
+ String hqlSelectNC = "from NameCounter where name = :name";
+ String hqlUpdateCounter = "update NameCounter set counter = :newCounter " +
+ "where name= :name " +
+ "and counter= :prevCounter";
+
+ Integer counter = null;
+ GenericUncheckedException lastException = null;
+ for (int i = 0; i< MAX_RETRIES_GETTING_COUNTER && counter==null; i++) {
+ try {
+ counter = calcCounter(name, hqlSelectNC, hqlUpdateCounter);
+ }
+ catch (GenericUncheckedException exception) {
+ lastException = exception; //do nothing, we will try again in the loop
+ }
+ }
+
+ if (counter!=null) {
+ return counter;
+ }
+
+ throw lastException!=null ? new DbFailureUncheckedException(lastException) :
+ new DbFailureUncheckedException("Failed to get counter for "+name+" due to unknown error");
+
+ }
+
+ private Integer calcCounter(String name, String hqlSelectNC, String hqlUpdateCounter) {
+ Integer counter;
+ counter = DaoUtils.tryWithSessionAndTransaction(sessionFactory, session -> {
+ NameCounter nameCounter = (NameCounter) session.createQuery(hqlSelectNC)
+ .setText("name", name)
+ .uniqueResult();
+ if (nameCounter != null) {
+ int updatedRows = session.createQuery(hqlUpdateCounter)
+ .setText("name", nameCounter.getName())
+ .setInteger("prevCounter", nameCounter.getCounter())
+ .setInteger("newCounter", nameCounter.getCounter() + 1)
+ .executeUpdate();
+ if (updatedRows == 1) {
+ return nameCounter.getCounter() + 1;
+ }
+ } else {
+ Object nameAsId = session.save(new NameCounter(name));
+ //if save success
+ if (nameAsId != null) {
+ return 1;
+ }
+ }
+ //in case of failure return null, in order to continue the loop
+ return null;
+ });
+ return counter;
+ }
+
+ @Override
+ public int getMaxRetriesGettingFreeNameFromAai() {
+ return maxRetriesGettingFreeNameFromAai;
+ }
+
+ @Override
+ public void setMaxRetriesGettingFreeNameFromAai(int maxRetriesGettingFreeNameFromAai) {
+ this.maxRetriesGettingFreeNameFromAai = maxRetriesGettingFreeNameFromAai;
+ }
+
+ @Override
+ public String getUniqueName(String name, ResourceType resourceType) {
+ //check that name aai response well before increasing counter from DB
+ //Prevents unnecessary increasing of the counter while AAI doesn't response
+ isNameFreeInAai(NAME_FOR_CHECK_AAI_STATUS, resourceType);
+
+ for (int i=0; i<getMaxRetriesGettingFreeNameFromAai(); i++) {
+ int counter = getCounterForName(name);
+ String newName = formatNameAndCounter(name, counter);
+ if (isNameFreeInAai(newName, resourceType)) {
+ return newName;
+ }
+ }
+
+ throw new MaxRetriesException("find unused name for "+name, getMaxRetriesGettingFreeNameFromAai());
+ }
+
+ //the method is protected so we can call it in the UT
+ protected String formatNameAndCounter(String name, int counter) {
+ return name + "_" + String.format("%03d", counter);
+ }
+
+ private boolean isNameFreeInAai(String name, ResourceType resourceType) throws InvalidAAIResponseException {
+ AaiResponse<AaiNodeQueryResponse> aaiResponse = aaiClient.searchNodeTypeByName(name, resourceType);
+ if (aaiResponse.getHttpCode() > 399 || aaiResponse.getT() == null) {
+ throw new InvalidAAIResponseException(aaiResponse);
+ }
+ return CollectionUtils.isEmpty(aaiResponse.getT().resultData);
+ }
+*/
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/DummyAsyncRequest.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/DummyAsyncRequest.java
new file mode 100644
index 0000000..22ce508
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/DummyAsyncRequest.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.onap.osam.job.IJobFactory;
+
+/**
+ * Currently - dummy class for demo purposes
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class DummyAsyncRequest implements IJobFactory.AsyncJobRequest {
+
+ @JsonProperty("dummyString")
+ private String dummyString;
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobData.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobData.java
new file mode 100644
index 0000000..4e9ceff
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobData.java
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import org.onap.osam.job.JobType;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+
+public class JobData {
+
+ private TreeMap<JobType, Map<String, Object>> commandData;
+ private JobSharedData sharedData;
+
+ public JobData() {
+ commandData = new TreeMap<>();
+ sharedData = new JobSharedData();
+ }
+
+ public JobData(TreeMap<JobType, Map<String, Object>> commandData, JobSharedData sharedData) {
+ this.commandData = commandData;
+ this.sharedData = sharedData;
+ }
+
+ public TreeMap<JobType, Map<String, Object>> getCommandData() {
+ return commandData;
+ }
+
+ public void setCommandData(TreeMap<JobType, Map<String, Object>> commandData) {
+ this.commandData = commandData;
+ }
+
+ public JobSharedData getSharedData() {
+ return sharedData;
+ }
+
+ public void setSharedData(JobSharedData sharedData) {
+ this.sharedData = sharedData;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof JobData)) return false;
+ JobData jobData = (JobData) o;
+ return Objects.equals(getCommandData(), jobData.getCommandData()) &&
+ Objects.equals(getSharedData(), jobData.getSharedData());
+ }
+
+ @Override
+ public int hashCode() {
+
+ return Objects.hash(getCommandData(), getSharedData());
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobFactory.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobFactory.java
new file mode 100644
index 0000000..a8ab0ce
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobFactory.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobFactory;
+import org.onap.osam.job.JobType;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.UUID;
+
+@Component
+public class JobFactory implements IJobFactory {
+
+ @Override
+ public OsamJob createRootJob(JobType jobType, AsyncJobRequest request, String userId, Integer indexInBulk, Map<String, Object> jobData){
+ OsamJob job = new OsamJob();
+ job.setStatus(JobStatus.PENDING);
+ job.setUuid(UUID.randomUUID());
+ job.setUserId(userId);
+ job.setTypeAndData(jobType, jobData);
+ job.setSharedData(new JobSharedData(job.getUuid(), userId, request));
+ job.setIndexInBulk(indexInBulk);
+ return job;
+ }
+
+ @Override
+ public OsamJob createChildJob(JobType jobType, JobStatus jobStatus, AsyncJobRequest request, JobSharedData parentSharedData, Map<String, Object> jobData) {
+ OsamJob job = new OsamJob();
+ job.setStatus(jobStatus);
+ job.setUuid(UUID.randomUUID());
+ job.setUserId(parentSharedData.getUserId());
+ job.setTypeAndData(jobType, jobData);
+ job.setSharedData(new JobSharedData(job.getUuid(), request, parentSharedData));
+ return job;
+ }
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobSchedulerInitializer.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobSchedulerInitializer.java
new file mode 100644
index 0000000..526b823
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobSchedulerInitializer.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.osam.common.exception.GenericUncheckedException;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.IJobsDataAccessService;
+import org.onap.osam.job.command.JobCommandFactory;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.SimpleTrigger;
+import org.quartz.TriggerBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
+
+@Slf4j
+@Component
+public class JobSchedulerInitializer {
+
+ private IJobsDataAccessService jobsDataAccessService;
+ private SchedulerFactoryBean schedulerFactoryBean;
+ private JobCommandFactory jobCommandFactory;
+
+ @Autowired
+ public JobSchedulerInitializer(
+ IJobsDataAccessService jobsDataAccessService,
+ SchedulerFactoryBean schedulerFactoryBean,
+ JobCommandFactory JobCommandFactory
+ ) {
+ this.jobsDataAccessService = jobsDataAccessService;
+ this.schedulerFactoryBean = schedulerFactoryBean;
+ this.jobCommandFactory = JobCommandFactory;
+
+ }
+
+ @PostConstruct
+ public void init() {
+ scheduleJobWorker(JobStatus.PENDING, 1);
+ scheduleJobWorker(JobStatus.CREATING, 1);
+ scheduleJobWorker(JobStatus.IN_PROGRESS, 1);
+ scheduleJobWorker(JobStatus.RESOURCE_IN_PROGRESS, 1);
+ }
+
+ private void scheduleJobWorker(JobStatus topic, int intervalInSeconds) {
+ final Scheduler scheduler = schedulerFactoryBean.getScheduler();
+ JobDetail jobDetail = JobBuilder.newJob().ofType(JobWorker.class)
+ .withIdentity("AsyncWorkersJob" + topic)
+ .withDescription("Job that run async worker for " + topic)
+ .setJobData(new JobDataMap(ImmutableMap.of(
+ "jobsDataAccessService", jobsDataAccessService,
+ "jobCommandFactory", jobCommandFactory,
+ "topic", topic
+ )))
+ .build();
+ SimpleTrigger asyncWorkerTrigger = TriggerBuilder.newTrigger().forJob(jobDetail)
+ .withIdentity("AsyncWorkersTrigger" + topic)
+ .withDescription("Trigger to run async worker for " + topic)
+ .withSchedule(simpleSchedule().repeatForever().withIntervalInSeconds(intervalInSeconds))
+ .build();
+ try {
+ scheduler.scheduleJob(jobDetail, asyncWorkerTrigger);
+ } catch (SchedulerException e) {
+ log.error("Failed to schedule trigger for async worker jobs: {}", e.getMessage());
+ throw new GenericUncheckedException(e);
+ }
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobSharedData.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobSharedData.java
new file mode 100644
index 0000000..2c16c95
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobSharedData.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.onap.osam.job.IJobFactory;
+
+import java.util.Objects;
+import java.util.UUID;
+
+public class JobSharedData {
+
+ protected UUID jobUuid;
+ protected String userId;
+ protected Class requestType;
+ protected UUID rootJobId;
+
+ @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, property="class")
+ protected IJobFactory.AsyncJobRequest request;
+
+ public JobSharedData() {
+ }
+
+ public JobSharedData(UUID jobUuid, String userId, IJobFactory.AsyncJobRequest request) {
+ this.jobUuid = jobUuid;
+ this.userId = userId;
+ this.requestType = request.getClass();
+ this.request = request;
+ this.rootJobId = jobUuid;
+ }
+
+ public JobSharedData(UUID jobUuid, IJobFactory.AsyncJobRequest request, JobSharedData parentData) {
+ this(jobUuid, parentData.getUserId(), request);
+ rootJobId = parentData.getRootJobId() != null ? parentData.getRootJobId() : parentData.getJobUuid();
+ }
+
+
+ public UUID getJobUuid() {
+ return jobUuid;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public Class getRequestType() {
+ return requestType;
+ }
+
+ public void setRequestType(Class requestType) {
+ this.requestType = requestType;
+ }
+
+ public IJobFactory.AsyncJobRequest getRequest() {
+ return request;
+ }
+
+ public void setRequest(IJobFactory.AsyncJobRequest request) {
+ this.request = request;
+ }
+
+ public UUID getRootJobId() {
+ return rootJobId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof JobSharedData)) return false;
+ JobSharedData that = (JobSharedData) o;
+ return Objects.equals(getJobUuid(), that.getJobUuid()) &&
+ Objects.equals(getUserId(), that.getUserId()) &&
+ Objects.equals(getRequestType(), that.getRequestType()) &&
+ Objects.equals(getRootJobId(), that.getRootJobId()) &&
+ Objects.equals(getRequest(), that.getRequest());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getJobUuid(), getUserId(), getRequestType(), getRootJobId(), getRequest());
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobWorker.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobWorker.java
new file mode 100644
index 0000000..97d52a5
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobWorker.java
@@ -0,0 +1,162 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.onap.osam.job.exceptions.JobException;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.IJobsDataAccessService;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.command.JobCommandFactory;
+import org.quartz.JobExecutionContext;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+import java.util.UUID;
+
+
+@Slf4j
+@Component
+public class JobWorker extends QuartzJobBean {
+
+ private IJobsDataAccessService jobsDataAccessService;
+ private JobCommandFactory jobCommandFactory;
+ private JobStatus topic;
+
+ @Override
+ protected void executeInternal(JobExecutionContext context) {
+ Optional<OsamJob> job;
+
+ job = pullJob();
+
+ while (job.isPresent()) {
+ OsamJob nextStateOfJob = executeJobAndGetNext(job.get());
+ pushBack(nextStateOfJob);
+ job = pullJob();
+ }
+ }
+
+ private Optional<OsamJob> pullJob() {
+ try {
+ return jobsDataAccessService.pull(topic, UUID.randomUUID().toString());
+ } catch (Exception e) {
+ log.error("failed to pull job from queue, breaking: {}", e, e);
+ tryMutingJobFromException(e);
+
+ return Optional.empty();
+ }
+ }
+
+ private void pushBack(OsamJob nextJob) {
+ try {
+ jobsDataAccessService.pushBack(nextJob);
+ } catch (Exception e) {
+ log.error("failed pushing back job to queue: {}", e, e);
+ }
+ }
+
+ protected OsamJob executeJobAndGetNext(OsamJob job) {
+ //TODO Pavel find out about teplateId
+ log.debug("going to execute job {}: {}/{}",
+ //log.debug("going to execute job {} of {}: {}/{}",
+ String.valueOf(job.getUuid()).substring(0,8),
+ //String.valueOf(job.getTemplateId()).substring(0, 8),
+ job.getStatus(), job.getType());
+
+ NextCommand nextCommand = executeCommandAndGetNext(job);
+
+ return setNextCommandInJob(nextCommand, job);
+ }
+
+ private NextCommand executeCommandAndGetNext(OsamJob job) {
+ NextCommand nextCommand;
+ try {
+ final IJobCommand jobCommand = jobCommandFactory.toCommand(job);
+ nextCommand = jobCommand.call();
+ } catch (Exception e) {
+ log.error("error while executing job from queue: {}", e);
+ nextCommand = new NextCommand(JobStatus.FAILED);
+ }
+
+ if (nextCommand == null) {
+ nextCommand = new NextCommand(JobStatus.STOPPED);
+ }
+ return nextCommand;
+ }
+
+ private OsamJob setNextCommandInJob(NextCommand nextCommand, OsamJob job) {
+ log.debug("transforming job {}: {}/{} -> {}{}",
+ String.valueOf(job.getUuid()).substring(0, 8),
+ job.getStatus(), job.getType(),
+ nextCommand.getStatus(),
+ nextCommand.getCommand() != null ? ("/" + nextCommand.getCommand().getType()) : "");
+
+ job.setStatus(nextCommand.getStatus());
+
+ if (nextCommand.getCommand() != null) {
+ job.setTypeAndData(nextCommand.getCommand().getType(), nextCommand.getCommand().getData());
+ }
+
+ return job;
+ }
+
+
+ private void tryMutingJobFromException(Exception e) {
+ // If there's JobException in the stack, read job uuid from
+ // the exception, and mute it in DB.
+ final int indexOfJobException =
+ ExceptionUtils.indexOfThrowable(e, JobException.class);
+
+ if (indexOfJobException >= 0) {
+ try {
+ final JobException jobException = (JobException) ExceptionUtils.getThrowableList(e).get(indexOfJobException);
+ log.info("muting job: {} ({})", jobException.getJobUuid(), jobException.toString());
+ final boolean success = jobsDataAccessService.mute(jobException.getJobUuid());
+ if (!success) {
+ log.error("failed to mute job {}", jobException.getJobUuid());
+ }
+ } catch (Exception e1) {
+ log.error("failed to mute job: {}", e1, e1);
+ }
+ }
+ }
+
+ //used by quartz to inject IJobsDataAccessService into the job
+ //see JobSchedulerInitializer
+ public void setJobsDataAccessService(IJobsDataAccessService jobsDataAccessService) {
+ this.jobsDataAccessService = jobsDataAccessService;
+ }
+
+ /*public void setFeatureManager(FeatureManager featureManager) {
+ this.featureManager = featureManager;
+ }*/
+
+ public void setJobCommandFactory(JobCommandFactory jobCommandFactory) {
+ this.jobCommandFactory = jobCommandFactory;
+ }
+
+ public void setTopic(JobStatus topic) {
+ this.topic = topic;
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobsDataAccessService.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobsDataAccessService.java
new file mode 100644
index 0000000..18ae378
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/impl/JobsDataAccessService.java
@@ -0,0 +1,211 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.osam.common.exception.GenericUncheckedException;
+import org.onap.osam.common.exception.InvalidOperationException;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobsDataAccessService;
+import org.onap.osam.job.repository.job.OsamJobRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.PostConstruct;
+import java.nio.ByteBuffer;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Optional;
+import java.util.UUID;
+
+@Slf4j
+@Service
+public class JobsDataAccessService implements IJobsDataAccessService {
+
+ private OsamJobRepository osamJobRepository;
+ private Long maxOpenedRequestsToAbstractOlt;
+ private int pollingIntervalSeconds;
+
+ @Autowired
+ public JobsDataAccessService(OsamJobRepository osamJobRepository,
+ @Value("0") Long maxOpenedRequestsToAbstractOlt,
+ @Value("10") int pollingIntervalSeconds) {
+ // tha @Value will inject conservative defaults; overridden in @PostConstruct from configuration
+ this.osamJobRepository = osamJobRepository;
+ this.maxOpenedRequestsToAbstractOlt = maxOpenedRequestsToAbstractOlt;
+ this.pollingIntervalSeconds = pollingIntervalSeconds;
+ }
+
+ @PostConstruct
+ public void configure() {
+ //TODO define defaults
+ /*maxOpenedRequestsToAbstractOlt = Integer.parseInt(System.getProperty(VidProperties.MSO_MAX_OPENED_INSTANTIATION_REQUESTS));
+ pollingIntervalSeconds = Integer.parseInt(System.getProperty(VidProperties.MSO_ASYNC_POLLING_INTERVAL_SECONDS));*/
+ }
+
+ public void deleteAll() {
+ osamJobRepository.deleteAll();
+ }
+
+ @Override
+ public UUID add(OsamJob job) {
+ osamJobRepository.save(job);
+ return job.getUuid();
+ }
+
+ @Override
+ public Optional<OsamJob> pull(JobStatus topic, String ownerId) {
+ OsamJob osamJob;
+ int updatedEntities;
+ do {
+
+ Optional<OsamJob> optionalOsamJob = selectQueryByJobStatus(topic);
+ if (!optionalOsamJob.isPresent()) {
+ return optionalOsamJob;
+ }
+
+ osamJob = optionalOsamJob.get();
+ final UUID uuid = osamJob.getUuid();
+ final Integer age = osamJob.getAge();
+
+ osamJob.setTakenBy(ownerId);
+
+ // It might become that a job was taken and pushed-back already, before we
+ // arrived here, so we're verifying the age was not pushed forward.
+ // Age is actually forwarded upon pushBack().
+ updatedEntities = osamJobRepository.updateOsamCoreJobsAge(ownerId, uuid, age);
+
+ } while (updatedEntities == 0);
+
+ return Optional.ofNullable(osamJob);
+ }
+
+ private java.sql.Timestamp nowMinusInterval() {
+ return Timestamp.valueOf(LocalDateTime.now().minusSeconds(pollingIntervalSeconds));
+ }
+
+ private Optional<OsamJob> selectQueryByJobStatus(JobStatus topic){
+ //TODO Pavel understand this interval
+ //String intervalCondition = (topic==JobStatus.CREATING) ? "" : (" and MODIFIED_DATE <= '" + nowMinusInterval()+"'");
+ return osamJobRepository.queryFirst1ByStatusAndTakenByIsNullAndDeletedAtIsNullOrderByModifiedDateAsc(topic).stream().findFirst();
+
+ }
+
+ private Optional<OsamJob> sqlQueryForTopic(JobStatus topic) {
+ switch (topic) {
+ case IN_PROGRESS:
+ case RESOURCE_IN_PROGRESS:
+ case CREATING:
+ case PENDING:
+ return selectQueryByJobStatus(topic);
+ //TODO Pavel - at first stage, using the naive query for pending topic
+ /*case PENDING:
+ return osamJobRepository.findOsamJobsPending(maxOpenedRequestsToAbstractOlt);*/
+ default:
+ throw new GenericUncheckedException("Unsupported topic to pull from: " + topic);
+ }
+ }
+
+
+ private byte[] getUuidAsByteArray(UUID owner) {
+ ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
+ bb.putLong(owner.getMostSignificantBits());
+ bb.putLong(owner.getLeastSignificantBits());
+ return bb.array();
+ }
+
+ @Override
+ public void pushBack(OsamJob job) {
+ final Optional<OsamJob> remoteDaoJob = osamJobRepository.findByUuid(job.getUuid());
+
+ if (!remoteDaoJob.isPresent()) {
+ throw new IllegalStateException("Can push back only pulled jobs. Add new jobs using add()");
+ }
+
+ if (remoteDaoJob.get().getTakenBy() == null) {
+ throw new IllegalStateException("Can push back only pulled jobs. This one already pushed back.");
+ }
+
+ job.setTakenBy(null);
+
+ Integer age = job.getAge();
+ job.setAge(age + 1);
+
+ log.debug("{}/{}", job.getStatus(), job.getType());
+
+ osamJobRepository.save(job);
+ }
+
+ /*private OsamJob castToOsamJob(OsamJob job) {
+ if (!(job instanceof OsamJob)) {
+ throw new UnsupportedOperationException("Can't add " + job.getClass() + " to " + this.getClass());
+ }
+ return (OsamJob) job;
+ }*/
+
+ @Override
+ public Collection<OsamJob> peek() {
+ return Lists.newArrayList(osamJobRepository.findAll());
+ }
+
+ @Override
+ public OsamJob peek(UUID jobId) {
+ return osamJobRepository.findByUuid(jobId).orElse(null);
+ }
+
+ @Override
+ public void delete(UUID jobId) {
+ Date now = new Date();
+ Integer updatedEntities = osamJobRepository.updateOsamCoreJobToBeDeleted(now, jobId, JobStatus.PENDING.toString(), JobStatus.STOPPED.toString());
+
+ if (updatedEntities == 0) {
+ final Optional<OsamJob> remoteDaoJob = osamJobRepository.findByUuid(jobId);
+
+ if (!remoteDaoJob.isPresent() || remoteDaoJob.get().getUuid() == null) {
+ log.debug("jobId {}: Service does not exist", jobId);
+ throw new InvalidOperationException("Service does not exist");
+ }
+
+ if (!remoteDaoJob.get().equals(JobStatus.PENDING) && !remoteDaoJob.get().getStatus().equals(JobStatus.STOPPED) || !StringUtils.isEmpty(remoteDaoJob.get().getTakenBy())) {
+ log.debug("jobId {}: Service status does not allow deletion from the queue, status = {}", jobId, remoteDaoJob.get().getStatus() +
+ ", takenBy " + remoteDaoJob.get().getTakenBy());
+ throw new InvalidOperationException("Service status does not allow deletion from the queue");
+ }
+
+ throw new InvalidOperationException("Service deletion failed");
+ }
+ }
+
+ @Override
+ public boolean mute(UUID jobId) {
+ if (jobId == null) {
+ return false;
+ }
+ final String prefix = "DUMP";
+ Integer updatedEntities = osamJobRepository.muteOsamCoreJob(jobId, prefix);
+ return updatedEntities != 0;
+ }
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/repository/job/OsamJobRepository.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/repository/job/OsamJobRepository.java
new file mode 100644
index 0000000..f473c5d
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/repository/job/OsamJobRepository.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.repository.job;
+
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+public interface OsamJobRepository extends CrudRepository<OsamJob, Long> {
+
+
+
+ Optional<OsamJob> findByUuid(UUID uuid);
+
+ List<OsamJob> findAllByUuid(Iterable<UUID> uuids);
+
+ //TODO Pavel add intervalCondition to the query
+ //String intervalCondition = (topic==JobStatus.CREATING) ? "" : (" and MODIFIED_DATE <= '" + nowMinusInterval()+"'");
+ //@Query("select o from OsamJob o where o.status = :status and o.takenBy is null and o.deletedAt is null order by o.modifiedDate asc")
+ //List<OsamJob> queryFirst1ByStatusAndTakenByIsNullAndDeleteAtIsNullOrderByModifiedDateAsc(@Param("status") JobStatus status);
+ List<OsamJob> queryFirst1ByStatusAndTakenByIsNullAndDeletedAtIsNullOrderByModifiedDateAsc(@Param("status") JobStatus status);
+
+ //Updates
+
+ @Transactional
+ @Modifying
+ @Query("update OsamJob job set job.takenBy = :takenBy where job.uuid = :uuid and job.age = :age and job.takenBy is null")
+ Integer updateOsamCoreJobsAge(@Param("takenBy") String takenBy, @Param("uuid") UUID uuid, @Param("age") Integer age);
+
+ @Transactional
+ @Modifying
+ @Query("update OsamJob job set job.deletedAt = :now where job.uuid = :uuid and job.status in(:pending, :stopped) and job.takenBy is null")
+ Integer updateOsamCoreJobToBeDeleted(@Param("now") Date date, @Param("uuid") UUID uuid, @Param("pending") String pending, @Param("stopped") String stopped);
+
+ @Transactional
+ @Modifying
+ @Query("update OsamJob job set job.status = concat(':prefix_',job.status), job.takenBy = null where job.uuid = :uuid and job.status not like ':prefix_%'")
+ Integer muteOsamCoreJob(@Param("uuid") UUID uuid, @Param("")String prefix);
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/utils/Streams.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/utils/Streams.java
new file mode 100644
index 0000000..35ffb98
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/utils/Streams.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.utils;
+
+import java.util.Iterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+public class Streams {
+ public static <R> Predicate<R> not(Predicate<R> predicate) {
+ return predicate.negate();
+ }
+
+ public static <T> Stream<T> fromIterator(final Iterator<T> iterator) {
+ Iterable<T> iterable = () -> iterator;
+ return StreamSupport.<T>stream(iterable.spliterator(), false);
+ }
+
+
+ // https://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate
+ private static <T> Spliterator<T> takeWhile(
+ Spliterator<T> splitr, Predicate<? super T> predicate) {
+ return new Spliterators.AbstractSpliterator<T>(splitr.estimateSize(), 0) {
+ boolean stillGoing = true;
+ @Override public boolean tryAdvance(Consumer<? super T> consumer) {
+ if (stillGoing) {
+ boolean hadNext = splitr.tryAdvance(elem -> {
+ if (predicate.test(elem)) {
+ consumer.accept(elem);
+ } else {
+ stillGoing = false;
+ }
+ });
+ return hadNext && stillGoing;
+ }
+ return false;
+ }
+ };
+ }
+
+ public static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<? super T> predicate) {
+ return StreamSupport.stream(takeWhile(stream.spliterator(), predicate), false);
+ }
+
+}
diff --git a/osam-core/async-jobs/src/main/java/org/onap/osam/job/utils/TimeUtils.java b/osam-core/async-jobs/src/main/java/org/onap/osam/job/utils/TimeUtils.java
new file mode 100644
index 0000000..bbd7ec3
--- /dev/null
+++ b/osam-core/async-jobs/src/main/java/org/onap/osam/job/utils/TimeUtils.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.utils;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class TimeUtils {
+ private static DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME;
+
+ private TimeUtils() {
+ // explicit private constructor, to hide the implicit public constructor
+ }
+
+ public static ZonedDateTime parseZonedDateTime(String time) {
+
+ return ZonedDateTime.from(formatter.parse(time));
+ }
+
+ public static String zonedDateTimeToString(ZonedDateTime time) {
+ return formatter.format(time);
+ }
+}
diff --git a/osam-core/async-jobs/src/test/java/org/onap/osam/job/command/JobCommandFactoryTest.java b/osam-core/async-jobs/src/test/java/org/onap/osam/job/command/JobCommandFactoryTest.java
new file mode 100644
index 0000000..0ea9933
--- /dev/null
+++ b/osam-core/async-jobs/src/test/java/org/onap/osam/job/command/JobCommandFactoryTest.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableMap;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobFactory;
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.JobType;
+import org.onap.osam.job.command.JobCommandFactory;
+import org.onap.osam.job.impl.JobSharedData;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class JobCommandFactoryTest {
+
+ private JobCommandFactory jobCommandFactory;
+
+ @Mock
+ private OsamJob job;
+
+ @Mock
+ private IJobCommand mockCommand;
+
+ @BeforeMethod
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @BeforeMethod
+ public void setUp() {
+ jobCommandFactory = new JobCommandFactory(any -> mockCommand);
+ }
+
+ @DataProvider
+ public Object[][] jobTypes() {
+ return Arrays.stream(
+ JobType.values()
+ ).map(v -> new Object[]{v}).collect(Collectors.toList()).toArray(new Object[][]{});
+
+ }
+
+ public static class MockedRequest implements IJobFactory.AsyncJobRequest {
+
+ final public int x;
+ final public String y;
+
+ @JsonCreator
+ public MockedRequest(@JsonProperty("x")int x, @JsonProperty("y")String y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof MockedRequest)) return false;
+ MockedRequest that = (MockedRequest) o;
+ return x == that.x &&
+ Objects.equals(y, that.y);
+ }
+
+ @Override
+ public int hashCode() {
+
+ return Objects.hash(x, y);
+ }
+ }
+
+ @Test(dataProvider = "jobTypes")
+ public void givenJob_createCommandCallsTheInitAndReturnsTheInstance(JobType jobType) {
+
+ final UUID uuid = UUID.randomUUID();
+ final Map<String, Object> data = ImmutableMap.of("foo", "bar");
+ final JobSharedData sharedData = new JobSharedData(uuid, "userid", new MockedRequest(1,"a"));
+
+ when(job.getType()).thenReturn(jobType);
+ when(job.getUuid()).thenReturn(uuid);
+ when(job.getDataMap()).thenReturn(data);
+ when(job.getSharedData()).thenReturn(sharedData);
+
+ final IJobCommand command = jobCommandFactory.toCommand(job);
+
+ verify(mockCommand).init(sharedData, data);
+
+ assertThat(command, equalTo(mockCommand));
+ }
+
+}
diff --git a/osam-core/async-jobs/src/test/java/org/onap/osam/job/command/WatchingCommandTest.java b/osam-core/async-jobs/src/test/java/org/onap/osam/job/command/WatchingCommandTest.java
new file mode 100644
index 0000000..0e595a6
--- /dev/null
+++ b/osam-core/async-jobs/src/test/java/org/onap/osam/job/command/WatchingCommandTest.java
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.command;
+
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobFactory;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.AsyncJobService;
+import org.onap.osam.job.command.WatchingCommand;
+import org.onap.osam.job.impl.JobSharedData;
+import org.onap.osam.job.repository.job.OsamJobRepository;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+public class WatchingCommandTest {
+
+ @Mock
+ private AsyncJobService asyncInstantiationBL;
+
+ @Mock
+ private OsamJobRepository osamJobRepository;
+
+ @InjectMocks
+ private WatchingCommand watchingCommand = new WatchingCommand();
+
+ @DataProvider
+ public static Object[][] expectedJobStatusDataProvider() {
+ return new Object[][]{
+ {Arrays.asList(JobStatus.COMPLETED, JobStatus.COMPLETED), JobStatus.COMPLETED, true},
+ {Arrays.asList(JobStatus.FAILED, JobStatus.COMPLETED), JobStatus.COMPLETED_WITH_ERRORS, true},
+ {Arrays.asList(JobStatus.STOPPED, JobStatus.COMPLETED), JobStatus.COMPLETED_WITH_ERRORS, false},
+ {Arrays.asList(JobStatus.IN_PROGRESS, JobStatus.FAILED), JobStatus.IN_PROGRESS, true},
+ {Arrays.asList(JobStatus.IN_PROGRESS, JobStatus.COMPLETED), JobStatus.IN_PROGRESS, true},
+ {Arrays.asList(JobStatus.IN_PROGRESS, JobStatus.IN_PROGRESS), JobStatus.IN_PROGRESS, true},
+ {Arrays.asList(null, JobStatus.COMPLETED), JobStatus.COMPLETED_WITH_ERRORS, true},
+ {Arrays.asList(null, JobStatus.IN_PROGRESS), JobStatus.IN_PROGRESS, true},
+ {Arrays.asList(null, JobStatus.FAILED), JobStatus.COMPLETED_WITH_ERRORS, false},
+ {new ArrayList<>(), JobStatus.COMPLETED, true}
+ };
+ }
+
+ @BeforeMethod
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test(dataProvider = "expectedJobStatusDataProvider")
+ public void testAssertNextCommandIsValid(List<JobStatus> childJobs, JobStatus expectedCommandStatus, boolean isService) {
+ //init sql result mock
+ List<OsamJob> mockChildren = childJobs.stream().map(st -> {
+ OsamJob job = new OsamJob();
+ job.setUuid(UUID.randomUUID());
+ job.setStatus(st);
+ return job;
+ }).collect(Collectors.toList());
+ when(osamJobRepository.findAllByUuid(any()))
+ .thenReturn(mockChildren);
+
+ //init job data for watching command
+ UUID jobUUID = UUID.randomUUID();
+ JobSharedData sharedData = new JobSharedData(jobUUID, "mockedUserID", mock(TestRequest.class));
+ List<UUID> uuids = mockChildren.stream().map(job -> job.getUuid()).collect(Collectors.toList());
+ watchingCommand.init(sharedData, uuids, isService);
+
+ //execute command and verify
+ NextCommand nextCommand = watchingCommand.call();
+ assertThat(nextCommand.getStatus(), is(expectedCommandStatus));
+ }
+
+ public static class TestRequest implements IJobFactory.AsyncJobRequest{}
+}
diff --git a/osam-core/async-jobs/src/test/java/org/onap/osam/job/impl/JobAdapterTest.java b/osam-core/async-jobs/src/test/java/org/onap/osam/job/impl/JobAdapterTest.java
new file mode 100644
index 0000000..289cd99
--- /dev/null
+++ b/osam-core/async-jobs/src/test/java/org/onap/osam/job/impl/JobAdapterTest.java
@@ -0,0 +1,113 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang3.RandomUtils;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobFactory;
+import org.onap.osam.job.JobType;
+import org.onap.osam.job.command.JobCommandFactoryTest;
+import org.testng.annotations.Test;
+
+import java.util.UUID;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+public class JobAdapterTest {
+
+ @Test
+ public void testCreateServiceInstantiationJob() {
+ IJobFactory jobAdapter = new JobFactory();
+
+ JobType jobType = JobType.NoOp;
+ IJobFactory.AsyncJobRequest request = new JobCommandFactoryTest.MockedRequest(42,"nothing");
+ String userId = "ou012t";
+ String optimisticUniqueServiceInstanceName = "optimisticUniqueServiceInstanceName";
+ int indexInBulk = RandomUtils.nextInt();
+ OsamJob job = jobAdapter.createRootJob(
+ jobType,
+ request,
+ userId,
+ indexInBulk,
+ ImmutableMap.of());
+
+ assertEquals(job.getType(), jobType);
+ assertEquals(job.getSharedData().getRequest(), request);
+ assertEquals(job.getSharedData().getRequestType(), request.getClass());
+ assertEquals(job.getSharedData().getUserId(), userId);
+ assertEquals(job.getSharedData().getJobUuid(), job.getUuid());
+ assertEquals(job.getSharedData().getRootJobId(), job.getUuid());
+ assertNotNull(job.getUuid());
+ assertEquals((int)job.getIndexInBulk(), indexInBulk );
+ assertEquals(job.getStatus(), JobStatus.PENDING);
+ }
+
+ @Test
+ public void testCreateChildJob() {
+
+ IJobFactory jobAdapter = new JobFactory();
+
+ String userId = "ou012t";
+ String optimisticUniqueServiceInstanceName = "optimisticUniqueServiceInstanceName";
+ int indexInBulk = RandomUtils.nextInt();
+ OsamJob grandJob = jobAdapter.createRootJob(
+ JobType.HttpCall,
+ new JobCommandFactoryTest.MockedRequest(99, "anything"),
+ userId,
+ indexInBulk,
+ ImmutableMap.of()
+ );
+
+ JobStatus jobStatus = JobStatus.PAUSE;
+ JobType jobType = JobType.NoOp;
+ IJobFactory.AsyncJobRequest request = new JobCommandFactoryTest.MockedRequest(42,"nothing");
+ OsamJob parentJob = jobAdapter.createChildJob(jobType, jobStatus, request, grandJob.getSharedData(), ImmutableMap.of());
+
+ assertEquals(parentJob.getType(), jobType);
+ assertEquals(parentJob.getSharedData().getRequest(), request);
+ assertEquals(parentJob.getSharedData().getRequestType(), request.getClass());
+ assertEquals(parentJob.getSharedData().getUserId(), userId);
+ assertEquals(parentJob.getSharedData().getJobUuid(), parentJob.getUuid());
+ assertNotNull(parentJob.getUuid());
+ assertNotEquals(parentJob.getUuid(), grandJob.getUuid());
+ assertEquals(parentJob.getStatus(), jobStatus);
+ assertEquals(parentJob.getSharedData().getRootJobId(), grandJob.getUuid());
+
+ JobStatus jobStatus2 = JobStatus.IN_PROGRESS;
+ JobType jobType2 = JobType.HttpCall;
+ IJobFactory.AsyncJobRequest request2 = new JobCommandFactoryTest.MockedRequest(66,"abc");
+ OsamJob job = jobAdapter.createChildJob(jobType2, jobStatus2, request2, parentJob.getSharedData(), ImmutableMap.of());
+
+ assertEquals(job.getType(), jobType2);
+ assertEquals(job.getSharedData().getRequest(), request2);
+ assertEquals(job.getSharedData().getRequestType(), request2.getClass());
+ assertEquals(job.getSharedData().getUserId(), userId);
+ assertEquals(job.getSharedData().getJobUuid(), job.getUuid());
+ assertNotNull(job.getUuid());
+ assertNotEquals(job.getUuid(), parentJob.getUuid());
+ assertEquals(job.getStatus(), jobStatus2);
+ assertEquals(job.getSharedData().getRootJobId(), grandJob.getUuid());
+
+ }
+}
diff --git a/osam-core/async-jobs/src/test/java/org/onap/osam/job/impl/JobWorkerTest.java b/osam-core/async-jobs/src/test/java/org/onap/osam/job/impl/JobWorkerTest.java
new file mode 100644
index 0000000..f38c238
--- /dev/null
+++ b/osam-core/async-jobs/src/test/java/org/onap/osam/job/impl/JobWorkerTest.java
@@ -0,0 +1,129 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.job.impl;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.hamcrest.Matcher;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.osam.job.dao.job.JobStatus;
+import org.onap.osam.job.dao.job.OsamJob;
+import org.onap.osam.job.IJobFactory;
+import org.onap.osam.job.IJobCommand;
+import org.onap.osam.job.JobType;
+import org.onap.osam.job.NextCommand;
+import org.onap.osam.job.command.HttpCallCommand;
+import org.onap.osam.job.command.JobCommandFactory;
+import org.onap.osam.job.impl.JobFactory;
+import org.onap.osam.job.impl.JobWorker;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.Map;
+import java.util.UUID;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class JobWorkerTest {
+
+
+ @Mock
+ private JobCommandFactory jobCommandFactory;
+
+ @InjectMocks
+ private JobWorker jobWorker = new JobWorker();
+
+ private final IJobCommand jobCommand = mock(IJobCommand.class);
+ private OsamJob jobUnderTest;
+ private IJobFactory.AsyncJobRequest originalData;
+ private JobType originalType;
+
+ @BeforeMethod
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+
+ when(jobCommandFactory.toCommand(any())).thenReturn(jobCommand);
+
+ originalData = new IJobFactory.AsyncJobRequest() {
+ public final Map datum = ImmutableMap.of("some", "data");
+ public final String foobar = "aux";
+ };
+
+ originalType = JobType.HttpCall;
+ jobUnderTest = new JobFactory().createRootJob(
+ originalType,
+ originalData,
+ "my user id",
+ RandomUtils.nextInt(),
+ ImmutableMap.of()
+ );
+ }
+
+ @Test
+ public void executeJobAndStepToNext_givenNull_onlyStatusModified() {
+
+ assertNextJobAfterExecuteJob(null, new String[]{"status"}, allOf(
+ hasProperty("status", is(JobStatus.STOPPED)),
+ hasProperty("sharedData", hasProperty("request", is(originalData))),
+ hasProperty("type", is(originalType)))
+ );
+ }
+
+ @Test
+ public void executeJobAndStepToNext_givenNextJob_jobDataIsModified() {
+
+ final JobStatus nextStatus = JobStatus.IN_PROGRESS;
+
+ final UUID jobUuid = UUID.randomUUID();
+ final NextCommand nextCommand = new NextCommand(nextStatus, new HttpCallCommand("my strange url", jobUuid));
+
+ String[] excludedFields = {"status", "data", "type"};
+
+ assertNextJobAfterExecuteJob(nextCommand, excludedFields, allOf(
+ hasProperty("status", is(nextStatus)),
+ hasProperty("dataMap", is(nextCommand.getCommand().getData())),
+ hasProperty("type", is(nextCommand.getCommand().getType())))
+ );
+ }
+
+ private void assertNextJobAfterExecuteJob(NextCommand nextCommand, String[] excludedFields, Matcher<OsamJob> jobMatcher) {
+ when(jobCommand.call()).thenReturn(nextCommand);
+
+ String jobBefore = new ReflectionToStringBuilder(jobUnderTest, ToStringStyle.SHORT_PREFIX_STYLE).setExcludeFieldNames(excludedFields).toString();
+
+ ////// FUNCTION UNDER TEST /////
+ OsamJob nextJob = jobWorker.executeJobAndGetNext(jobUnderTest);
+ ////////////////////////////////
+
+ String jobAfter = new ReflectionToStringBuilder(nextJob, ToStringStyle.SHORT_PREFIX_STYLE).setExcludeFieldNames(excludedFields).toString();
+
+ assertThat(nextJob, jobMatcher);
+ assertThat(jobAfter, equalTo(jobBefore));
+ }
+}
diff --git a/osam-core/common/src/main/java/org/onap/osam/common/exception/GenericUncheckedException.java b/osam-core/common/src/main/java/org/onap/osam/common/exception/GenericUncheckedException.java
new file mode 100644
index 0000000..4db0147
--- /dev/null
+++ b/osam-core/common/src/main/java/org/onap/osam/common/exception/GenericUncheckedException.java
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.osam.common.exception;
+
+public class GenericUncheckedException extends RuntimeException {
+ public GenericUncheckedException(String message) {
+ super(message);
+ }
+
+ public GenericUncheckedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public GenericUncheckedException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/osam-core/external/pom.xml b/osam-core/external/pom.xml
index dd70a2c..13724aa 100644
--- a/osam-core/external/pom.xml
+++ b/osam-core/external/pom.xml
@@ -56,11 +56,6 @@
<artifactId>api</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>${guava.version}</version>
- </dependency>
</dependencies>
<pluginRepositories>
<pluginRepository>
diff --git a/osam-core/model/src/main/resources/application.properties b/osam-core/model/src/main/resources/application.properties
index f03a78a..81ed287 100644
--- a/osam-core/model/src/main/resources/application.properties
+++ b/osam-core/model/src/main/resources/application.properties
@@ -1,9 +1,10 @@
spring.datasource.url=jdbc:mariadb://localhost:3306/osam_core
spring.datasource.username=root
spring.datasource.password=123456
+
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
-logging.level.org.hibernate.SQL=DEBUG
-logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
-logging.level.org.hibernate.type=TRACE
\ No newline at end of file
+logging.level.org.onap.osam=DEBUG
+#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
+#logging.level.org.hibernate.type=TRACE
\ No newline at end of file
diff --git a/osam-core/pom.xml b/osam-core/pom.xml
index bd6beef..c207ad8 100644
--- a/osam-core/pom.xml
+++ b/osam-core/pom.xml
@@ -14,6 +14,7 @@
</parent>
<modules>
<module>common</module>
+ <module>async-jobs</module>
<module>model</module>
<module>api</module>
<module>external</module>
@@ -44,11 +45,30 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${guava.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.14.3</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<dependencyManagement>
<dependencies>
@@ -67,6 +87,11 @@
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>async-jobs</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>external</artifactId>
diff --git a/osam-core/web/pom.xml b/osam-core/web/pom.xml
index e8bd078..62d4473 100644
--- a/osam-core/web/pom.xml
+++ b/osam-core/web/pom.xml
@@ -35,9 +35,12 @@
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <!-- For the async jobs demo controller -->
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
+ <groupId>org.onap.osam</groupId>
+ <artifactId>async-jobs</artifactId>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
diff --git a/osam-core/web/src/main/java/org/onap/osam/controller/AsyncJobDemoController.java b/osam-core/web/src/main/java/org/onap/osam/controller/AsyncJobDemoController.java
new file mode 100644
index 0000000..77936e5
--- /dev/null
+++ b/osam-core/web/src/main/java/org/onap/osam/controller/AsyncJobDemoController.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OSAM
+ * ================================================================================
+ * Copyright (C) 2018 AT&T
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.osam.controller;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.osam.job.AsyncJobService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.jackson.JsonComponent;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.UUID;
+
+@RestController
+@RequestMapping("/async")
+@Slf4j
+public class AsyncJobDemoController extends AbstractController{
+ private AsyncJobService asyncJobService;
+
+ @Autowired
+ public AsyncJobDemoController(AsyncJobService asyncJobService) {
+ super(log);
+ this.asyncJobService = asyncJobService;
+ }
+
+ @PostMapping("/chassis")
+ public ResponseEntity<String> createChassisWithAsyncJob(@RequestBody AsyncJobDemoControllerData data){
+ try {
+ final List<UUID> asyncJob = asyncJobService.pushBulkJob("demoUser", data.getIsRootJobSuccessful(), data.getIsRootDependantOnChildren());
+ return new ResponseEntity<String>(asyncJob.get(0).toString(), HttpStatus.OK);
+ }catch (Exception e){
+ return super.proceedException(e);
+ }
+ }
+
+
+ @AllArgsConstructor
+ @NoArgsConstructor
+ @Setter
+ @Getter
+ @JsonAutoDetect
+ public static class AsyncJobDemoControllerData {
+ private Boolean isRootJobSuccessful;
+ private Boolean isRootDependantOnChildren;
+ }
+}