001package com.identityworksllc.iiq.common.service;
002
003import org.apache.commons.logging.Log;
004import org.apache.commons.logging.LogFactory;
005import sailpoint.api.SailPointContext;
006import sailpoint.server.BasePluginService;
007import sailpoint.server.Service;
008import sailpoint.tools.GeneralException;
009
010import java.util.concurrent.atomic.AtomicBoolean;
011import java.util.concurrent.atomic.AtomicLong;
012
013/**
014 * Abstract super-class for services. This class provides some minimal
015 * services, such as tracking execution counts and last execution times,
016 * then delegates to the {@link #implementation(SailPointContext)} method.
017 */
018public abstract class BaseCommonService extends Service implements BaseServiceImplementation {
019
020    /**
021     * The logger for use by this class
022     */
023    private static final Log _bcpsLogger = LogFactory.getLog(BaseCommonService.class);
024
025    /**
026     * The execution count for this service, available to sub-classes
027     */
028    protected AtomicBoolean executedOnce;
029
030    /**
031     * The execution count for this service, available to sub-classes
032     */
033    protected AtomicLong executionCount;
034
035    /**
036     * The last start time, stored by default in {@link #beforeExecution(SailPointContext)}
037     */
038    private final AtomicLong lastStartTime;
039
040    /**
041     * The actual callback to the user's implementation for this service
042     */
043    protected ServiceImplementationInvoker invoker;
044
045    /**
046     * Base common plugin service constructor
047     */
048    protected BaseCommonService() {
049        this.executedOnce = new AtomicBoolean();
050        this.executionCount = new AtomicLong();
051        this.lastStartTime = new AtomicLong();
052        this.invoker = this::implementation;
053    }
054
055    /**
056     * @see BaseServiceImplementation#afterExecution(SailPointContext) 
057     */
058    @Override
059    public void afterExecution(SailPointContext context) throws GeneralException {
060        this.incrementExecutions();
061
062        ServiceUtils.storeTimestamps(context, getDefinition(), this.lastStartTime.get());
063    }
064
065    /**
066     * @see BaseServiceImplementation#beforeExecution(SailPointContext) (SailPointContext)
067     */
068    @Override
069    public void beforeExecution(SailPointContext context) throws GeneralException {
070        this.lastStartTime.set(System.currentTimeMillis());
071    }
072
073    /**
074     * The main entry point of the service
075     * 
076     * @see Service#execute(SailPointContext) 
077     * @param context The IIQ context for this service run
078     * @throws GeneralException if the service execution failed
079     */
080    @Override
081    public final void execute(SailPointContext context) throws GeneralException {
082        beforeExecution(context);
083
084        if (this.executionCount.get() >= skipExecutionCount()) {
085            this.invoker.invokeImplementation(context);
086        } else {
087            _bcpsLogger.info("Skipping execution (count = " + this.executionCount.get() + ") of service " + getDefinition().getName());
088        }
089
090        afterExecution(context);
091    }
092
093    /**
094     * Returns the elapsed runtime of this service, in milliseconds. Services
095     * should use this method to stop after a certain timeout period has been
096     * reached to avoid bogging down the {@link sailpoint.server.Servicer}.
097     *
098     * @return The elapsed runtime of this service, in milliseconds
099     */
100    protected long getElapsedRuntimeMillis() {
101        return (System.currentTimeMillis() - this.lastStartTime.get());
102    }
103    
104    /**
105     * Increments the execution count and sets the executed-once flag to true
106     */
107    @Override
108    public final void incrementExecutions() {
109        this.executedOnce.set(true);
110        this.executionCount.incrementAndGet();
111    }
112
113}