aboutsummaryrefslogtreecommitdiffstats
path: root/context/context-management/src/main/java/org/onap/policy/apex/context/impl/locking/AbstractLockManager.java
blob: 2ef229193b52b81b38d61ac0f778b0adaa3ab392 (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
/*-
 * ============LICENSE_START=======================================================
 *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
 *  Modifications Copyright (C) 2021 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.
 *
 * SPDX-License-Identifier: Apache-2.0
 * ============LICENSE_END=========================================================
 */

package org.onap.policy.apex.context.impl.locking;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import lombok.Getter;
import org.onap.policy.apex.context.ContextException;
import org.onap.policy.apex.context.LockManager;
import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
import org.slf4j.ext.XLogger;
import org.slf4j.ext.XLoggerFactory;

/**
 * This class implements the {@link LockManager} functionality that is common across all implementations. Lock managers
 * for specific lock mechanisms specialize this class.
 *
 * @author Liam Fallon (liam.fallon@ericsson.com)
 */
public abstract class AbstractLockManager implements LockManager {
    // Logger for this class
    private static final XLogger LOGGER = XLoggerFactory.getXLogger(AbstractLockManager.class);

    // Recurring string constants
    private static final String CONTEXT_ITEM = " context item ";

    // The key of this lock manager
    @Getter
    private AxArtifactKey key = null;

    // Map of locks in use on this distributor for each context map
    private final Map<String, Map<String, ReadWriteLock>> lockMaps = Collections
                    .synchronizedMap(new HashMap<String, Map<String, ReadWriteLock>>());

    /**
     * {@inheritDoc}.
     */
    @Override
    public void init(final AxArtifactKey lockManagerKey) throws ContextException {
        this.key = lockManagerKey;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public synchronized void lockForReading(final String lockTypeKey, final String lockKey) throws ContextException {
        LOGGER.entry("lockForReading(" + lockTypeKey + "_" + lockKey + ")");

        // Find the lock or create a new one
        final ReadWriteLock lock = getLock(lockTypeKey, lockKey, true);

        try {
            lock.readLock().lock();
            LOGGER.exit("lockForReading(" + lockTypeKey + "_" + lockKey + ")");
        } catch (final Exception e) {
            LOGGER.warn("error acquiring read lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey, e);
            throw new ContextException(
                            "error acquiring read lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey, e);
        }
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public synchronized void lockForWriting(final String lockTypeKey, final String lockKey) throws ContextException {
        LOGGER.entry("lockForWriting(" + lockTypeKey + "_" + lockKey + ")");

        // Find the lock or create a new one
        final ReadWriteLock lock = getLock(lockTypeKey, lockKey, true);

        try {
            lock.writeLock().lock();
            LOGGER.exit("lockForWriting(" + lockTypeKey + "_" + lockKey + ")");
        } catch (final Exception e) {
            LOGGER.warn("error acquiring write lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey, e);
            throw new ContextException(
                            "error acquiring write lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey, e);
        }
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void unlockForReading(final String lockTypeKey, final String lockKey) throws ContextException {
        LOGGER.entry("unlockForReading(" + lockTypeKey + "_" + lockKey + ")");

        // Find the lock
        final ReadWriteLock lock = getLock(lockTypeKey, lockKey, false);

        try {
            lock.readLock().unlock();
            LOGGER.exit("unlockForReading(" + lockTypeKey + "_" + lockKey + ")");
        } catch (final Exception e) {
            LOGGER.warn("error releasing read lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey, e);
            throw new ContextException(
                            "error releasing read lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey, e);
        }
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void unlockForWriting(final String lockTypeKey, final String lockKey) throws ContextException {
        LOGGER.entry("unlockForWriting(" + lockTypeKey + "_" + lockKey + ")");

        // Find the lock
        final ReadWriteLock lock = getLock(lockTypeKey, lockKey, false);

        try {
            lock.writeLock().unlock();
            LOGGER.exit("unlockForWriting(" + lockTypeKey + "_" + lockKey + ")");
        } catch (final Exception e) {
            LOGGER.warn("error releasing write lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey, e);
            throw new ContextException(
                            "error releasing write lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey, e);
        }
    }

    /**
     * Get a reentrant read write lock from whatever locking mechanism is in use.
     *
     * @param lockId The unique ID of the lock.
     * @return The lock
     * @throws ContextException On errors getting a lock
     */
    protected abstract ReadWriteLock getReentrantReadWriteLock(String lockId) throws ContextException;

    /**
     * Get a lock for a context item in a context map.
     *
     * @param lockTypeKey The key of the map where the context item to lock is
     * @param lockKey The key on the map to lock
     * @param createMode if true, create a lock if it does not exist
     * @return The lock
     * @throws ContextException On errors getting the lock
     */
    private ReadWriteLock getLock(final String lockTypeKey, final String lockKey, final boolean createMode)
                    throws ContextException {
        // Find or create a map for the lock type
        Map<String, ReadWriteLock> lockTypeMap = lockMaps.computeIfAbsent(lockTypeKey,
            unusedKey -> Collections.synchronizedMap(new HashMap<String, ReadWriteLock>()));

        // Find or create a lock in the lock map
        ReadWriteLock lock = lockTypeMap.get(lockKey);
        if (lock != null) {
            return lock;
        }

        // Should we create a lock?
        String errorMessage = "error getting lock on context map " + lockTypeKey + CONTEXT_ITEM + lockKey;
        if (!createMode) {
            String message = errorMessage + ", lock does not exist";
            LOGGER.warn(message);
            throw new ContextException(message);
        }

        try {
            // Create the lock using the specialization of this abstract class
            lock = getReentrantReadWriteLock(lockTypeKey + "_" + lockKey);

            // Add the lock to the lock map
            lockTypeMap.put(lockKey, lock);

            if (LOGGER.isTraceEnabled()) {
                LOGGER.trace("created lock {}_{}", lockTypeKey, lockKey);
            }
            return lock;
        } catch (final Exception e) {
            LOGGER.warn(errorMessage, e);
            throw new ContextException(errorMessage, e);
        }
    }
}