Async jobs mechanism migrated from VID code

Change-Id: Icf8bbac9ad83addcef2269839fb66f8c1ad31c5a
Signed-off-by: Aharoni, Pavel (pa0916) <pavel.aharoni@intl.att.com>
diff --git a/onap-enabler-be/pom.xml b/onap-enabler-be/pom.xml
index 9952caf..b1ced0a 100644
--- a/onap-enabler-be/pom.xml
+++ b/onap-enabler-be/pom.xml
@@ -81,7 +81,6 @@
             <name>Maven2 repo</name>

             <url>https://repo.maven.apache.org/maven2</url>

         </repository>

-

 	</repositories>

 	<distributionManagement>

 		<repository>

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;

+    }

+}