aboutsummaryrefslogtreecommitdiffstats
path: root/policy-management/src/main/java/org/onap/policy/drools/system/internal/FeatureLockImpl.java
blob: 6b0c8a434a3913ad584ffed9fff74606ec33e326 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
 * ============LICENSE_START=======================================================
 * ONAP
 * ================================================================================
 * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
 * ================================================================================
 * 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.policy.drools.system.internal;

import java.util.concurrent.ScheduledExecutorService;
import org.onap.policy.drools.core.DroolsRunnable;
import org.onap.policy.drools.core.PolicySession;
import org.onap.policy.drools.core.lock.LockCallback;
import org.onap.policy.drools.core.lock.LockImpl;
import org.onap.policy.drools.core.lock.LockState;
import org.onap.policy.drools.system.PolicyEngineConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Lock implementation used by locking features.
 */
public abstract class FeatureLockImpl extends LockImpl {
    private static final long serialVersionUID = 1L;
    private static final Logger logger = LoggerFactory.getLogger(FeatureLockImpl.class);

    public static final String LOCK_LOST_MSG = "lock lost";

    /**
     * {@code True} if this lock is attached to a feature, {@code false} if it is not.
     */
    private transient boolean attached;

    /**
     * Constructs the object.
     */
    public FeatureLockImpl() {
        this.attached = false;
    }

    /**
     * Constructs the object.
     *
     * @param state initial state of the lock
     * @param resourceId identifier of the resource to be locked
     * @param ownerKey information identifying the owner requesting the lock
     * @param holdSec amount of time, in seconds, for which the lock should be held, after
     *        which it will automatically be released
     * @param callback callback to be invoked once the lock is granted, or subsequently
     *        lost; must not be {@code null}
     */
    public FeatureLockImpl(LockState state, String resourceId, String ownerKey, int holdSec, LockCallback callback) {
        super(state, resourceId, ownerKey, holdSec, callback);
        this.attached = true;
    }

    /**
     * Grants this lock.
     */
    protected synchronized void grant() {
        if (isUnavailable()) {
            return;
        }

        setState(LockState.ACTIVE);
        updateGrant();

        logger.info("lock granted: {}", this);
        doNotify(this::notifyAvailable);
    }

    /**
     * Permanently denies this lock.
     *
     * @param reason the reason the lock was denied
     */
    public void deny(String reason) {
        synchronized (this) {
            setState(LockState.UNAVAILABLE);
        }

        logger.info("{}: {}", reason, this);
        doNotify(this::notifyUnavailable);
    }

    /**
     * Notifies the session of a change in the lock state. If a session is attached, then
     * it simply injects the notifier into the session. Otherwise, it executes it via a
     * background thread.
     *
     * @param notifier function to invoke the callback
     */
    private void doNotify(DroolsRunnable notifier) {
        PolicySession sess = getSession();
        if (sess != null) {
            sess.insertDrools(notifier);

        } else {
            getThreadPool().execute(notifier);
        }
    }

    /**
     * Determines if the lock can be freed.
     *
     * @return {@code true} if the lock can be freed, {@code false} if the lock is
     *         unavailable
     */
    protected boolean freeAllowed() {
        // do a quick check of the state
        if (isUnavailable()) {
            return false;
        }

        logger.info("releasing lock: {}", this);

        if (!attachFeature()) {
            setState(LockState.UNAVAILABLE);
            return false;
        }

        return true;
    }

    /**
     * The subclass should make use of {@link #extendAllowed()} in its implementation of
     * {@link #extend()}.
     */
    @Override
    public abstract void extend(int holdSec, LockCallback callback);

    /**
     * Determines if the lock can be extended.
     *
     * @param holdSec the additional amount of time to hold the lock, in seconds
     * @param callback callback to be invoked when the extension completes
     * @return {@code true} if the lock can be extended, {@code false} if the lock is
     *         unavailable
     */
    protected boolean extendAllowed(int holdSec, LockCallback callback) {
        if (holdSec < 0) {
            throw new IllegalArgumentException("holdSec is negative");
        }

        setHoldSec(holdSec);
        setCallback(callback);

        // do a quick check of the state
        if (isUnavailable() || !attachFeature()) {
            deny(LOCK_LOST_MSG);
            return false;
        }

        return true;
    }

    /**
     * Attaches to the feature instance, if not already attached.
     *
     * @return {@code true} if the lock is now attached to a feature, {@code false}
     *         otherwise
     */
    private synchronized boolean attachFeature() {
        if (!attached) {
            attached = addToFeature();
        }

        return attached;
    }

    /**
     * Updates a lock when it is granted. The default method does nothing.
     */
    protected void updateGrant() {
        // do nothing
    }

    /**
     * Adds the lock to the relevant feature.
     *
     * @return {@code true} if the lock was added, {@code false} if it could not be added
     *         (e.g., because there is no feature yet)
     */
    protected abstract boolean addToFeature();

    // these may be overridden by junit tests

    protected ScheduledExecutorService getThreadPool() {
        return PolicyEngineConstants.getManager().getExecutorService();
    }

    protected PolicySession getSession() {
        return PolicySession.getCurrentSession();
    }
}