• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / elemental / 982

29 Apr 2025 08:34PM UTC coverage: 56.409% (+0.007%) from 56.402%
982

push

circleci

adamretter
[feature] Improve README.md badges

28451 of 55847 branches covered (50.94%)

Branch coverage included in aggregate %.

77468 of 131924 relevant lines covered (58.72%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.99
/exist-core/src/main/java/org/exist/storage/BrokerPoolServicesManager.java
1
/*
2
 * Elemental
3
 * Copyright (C) 2024, Evolved Binary Ltd
4
 *
5
 * admin@evolvedbinary.com
6
 * https://www.evolvedbinary.com | https://www.elemental.xyz
7
 *
8
 * Use of this software is governed by the Business Source License 1.1
9
 * included in the LICENSE file and at www.mariadb.com/bsl11.
10
 *
11
 * Change Date: 2028-04-27
12
 *
13
 * On the date above, in accordance with the Business Source License, use
14
 * of this software will be governed by the Apache License, Version 2.0.
15
 *
16
 * Additional Use Grant: Production use of the Licensed Work for a permitted
17
 * purpose. A Permitted Purpose is any purpose other than a Competing Use.
18
 * A Competing Use means making the Software available to others in a commercial
19
 * product or service that: substitutes for the Software; substitutes for any
20
 * other product or service we offer using the Software that exists as of the
21
 * date we make the Software available; or offers the same or substantially
22
 * similar functionality as the Software.
23
 */
24
package org.exist.storage;
25

26
import net.jcip.annotations.NotThreadSafe;
27
import org.exist.storage.txn.Txn;
28
import org.exist.util.Configuration;
29
import com.evolvedbinary.j8fu.fsm.AtomicFSM;
30
import com.evolvedbinary.j8fu.fsm.FSM;
31

32
import static com.evolvedbinary.j8fu.fsm.TransitionTable.transitionTable;
33
import static org.exist.security.UnixStylePermission.LOG;
34

35

36
import java.util.ArrayList;
37
import java.util.List;
38

39
/**
40
 * This class simply maintains a list of {@link BrokerPoolService}
41
 * and provides methods to {@BrokerPool} to manage the lifecycle of
42
 * those services.
43
 *
44
 * This class should only be accessed from {@link BrokerPool}
45
 * and the order of method invocation (service state change)
46
 * is significant and must follow the startup order:
47
 *
48
 *      register -> configure -> prepare ->
49
 *          pre-system -> system -> pre-multi-user -> multi-user
50
 *
51
 * The shutdown order must likewise follow:
52
 *
53
 *      stop-multi-user -> stop-system -> shutdown
54
 *
55
 *
56
 * @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
57
 */
58
@NotThreadSafe
59
class BrokerPoolServicesManager {
1✔
60

61
    private enum ManagerState {
1✔
62
        REGISTRATION,
1✔
63
        CONFIGURATION,
1✔
64
        PREPARATION,
1✔
65
        PRE_SYSTEM,
1✔
66
        SYSTEM,
1✔
67
        PRE_MULTI_USER,
1✔
68
        MULTI_USER,
1✔
69
        STOPPING_MULTI_USER,
1✔
70
        STOPPING_SYSTEM,
1✔
71
        SHUTTING_DOWN
1✔
72
    }
73

74
    private enum ManagerEvent {
1✔
75
        CONFIGURE,
1✔
76
        PREPARE,
1✔
77
        PREPARE_ENTER_SYSTEM_MODE,
1✔
78
        ENTER_SYSTEM_MODE,
1✔
79
        PREPARE_ENTER_MULTI_USER_MODE,
1✔
80
        ENTER_MULTI_USER_MODE,
1✔
81
        STOP_MULTI_USER_MODE,
1✔
82
        STOP_SYSTEM_MODE,
1✔
83
        SHUTDOWN
1✔
84
    }
85

86
    @SuppressWarnings("unchecked")
87
    private FSM<ManagerState, ManagerEvent> states = new AtomicFSM<>(ManagerState.REGISTRATION, transitionTable(ManagerState.class, ManagerEvent.class)
1✔
88
            .when(ManagerState.REGISTRATION)
1✔
89
            .on(ManagerEvent.CONFIGURE).switchTo(ManagerState.CONFIGURATION)
1✔
90
            .on(ManagerEvent.PREPARE).switchTo(ManagerState.PREPARATION)
1✔
91
            .on(ManagerEvent.PREPARE_ENTER_SYSTEM_MODE).switchTo(ManagerState.PRE_SYSTEM)
1✔
92
            .on(ManagerEvent.ENTER_SYSTEM_MODE).switchTo(ManagerState.SYSTEM)
1✔
93
            .on(ManagerEvent.PREPARE_ENTER_MULTI_USER_MODE).switchTo(ManagerState.PRE_MULTI_USER)
1✔
94
            .on(ManagerEvent.ENTER_MULTI_USER_MODE).switchTo(ManagerState.MULTI_USER)
1✔
95
            .on(ManagerEvent.STOP_MULTI_USER_MODE).switchTo(ManagerState.STOPPING_MULTI_USER)
1✔
96
            .on(ManagerEvent.STOP_SYSTEM_MODE).switchTo(ManagerState.STOPPING_SYSTEM)
1✔
97
            .on(ManagerEvent.SHUTDOWN).switchTo(ManagerState.SHUTTING_DOWN)
1✔
98
            .build()
1✔
99
    );
100

101
    final List<BrokerPoolService> brokerPoolServices = new ArrayList<>();
1✔
102

103
    /**
104
     * Register a Service to be managed
105
     *
106
     * Note all services must be registered before any service is configured
107
     * failure to do so will result in an {@link IllegalStateException}
108
     *
109
     * @param brokerPoolService The service to be managed
110
     *
111
     * @return The service after it has been registered
112
     *
113
     * @throws IllegalStateException Thrown if there is an attempt to register a service
114
     * after any other service has been configured.
115
     */
116
    <T extends BrokerPoolService> T register(final T brokerPoolService) {
117
        final ManagerState currentState = states.getCurrentState();
1✔
118
        if(currentState != ManagerState.REGISTRATION) {
1!
119
            throw new IllegalStateException(
×
120
                    "Services may only be registered during the registration state. Current state is: " + currentState.name());
×
121
        }
122

123
        brokerPoolServices.add(brokerPoolService);
1✔
124
        if(LOG.isTraceEnabled()) {
1!
125
            LOG.trace("Registered service: {}...", brokerPoolService.getClass().getSimpleName());
×
126
        }
127
        return brokerPoolService;
1✔
128
    }
129

130
    /**
131
     * Configures the Services
132
     *
133
     * Expected to be called from {@link BrokerPool#initialize()}
134
     *
135
     * @param configuration The database configuration (i.e. conf.xml)
136
     *
137
     * @throws BrokerPoolServiceException if any service causes an error during configuration
138
     *
139
     * @throws IllegalStateException Thrown if there is an attempt to configure a service
140
     * after any other service has been prepared.
141
     */
142
    void configureServices(final Configuration configuration) throws BrokerPoolServiceException {
143
        states.process(ManagerEvent.CONFIGURE);
1✔
144

145
        for(final BrokerPoolService brokerPoolService : brokerPoolServices) {
1✔
146
            if(LOG.isTraceEnabled()) {
1!
147
                LOG.trace("Configuring service: {}...", brokerPoolService.getClass().getSimpleName());
×
148
            }
149
            brokerPoolService.configure(configuration);
1✔
150
        }
151
    }
1✔
152

153
    /**
154
     * Prepare the Services for system (single user) mode
155
     *
156
     * Prepare is called before the BrokerPool enters
157
     * system (single user) mode. As yet there are still
158
     * no brokers!
159
     *
160
     * @throws BrokerPoolServiceException if any service causes an error during preparation
161
     *
162
     * @throws IllegalStateException Thrown if there is an attempt to prepare a service
163
     * after any other service has entered start system service.
164
     */
165
    void prepareServices(final BrokerPool brokerPool) throws BrokerPoolServiceException {
166
        states.process(ManagerEvent.PREPARE);
1✔
167

168
        for(final BrokerPoolService brokerPoolService : brokerPoolServices) {
1✔
169
            if(LOG.isTraceEnabled()) {
1!
170
                LOG.trace("Preparing service: {}...", brokerPoolService.getClass().getSimpleName());
×
171
            }
172
            brokerPoolService.prepare(brokerPool);
1✔
173
        }
174
    }
1✔
175

176
    /**
177
     * Starts any services which should be started directly before
178
     * the database enters system mode, but before any general system
179
     * mode operations are performed.
180
     *
181
     * At this point the broker pool is in system (single user) mode
182
     * and not generally available for access, only a single
183
     * system broker is available, no system services have been started
184
     *
185
     * @param systemBroker The System Broker which is available for
186
     *   services to use to access the database
187
     * @param transaction The transaction for the system services
188
     *
189
     * @throws BrokerPoolServiceException if any service causes an error during starting the pre system mode
190
     *
191
     * @throws IllegalStateException Thrown if there is an attempt to start a service
192
     * after any other service has entered the start pre-system mode.
193
     */
194
    void startPreSystemServices(final DBBroker systemBroker, final Txn transaction) throws BrokerPoolServiceException {
195
        states.process(ManagerEvent.PREPARE_ENTER_SYSTEM_MODE);
1✔
196
        for(final BrokerPoolService brokerPoolService : brokerPoolServices) {
1✔
197
            brokerPoolService.startPreSystem(systemBroker, transaction);
1✔
198
        }
199
    }
1✔
200

201
    /**
202
     * Starts any services which should be started directly after
203
     * the database enters system mode, but before any system mode
204
     * operations are performed.
205
     *
206
     * At this point the broker pool is in system (single user) mode
207
     * and not generally available for access, only a single
208
     * system broker is available.
209
     *
210
     * @param systemBroker The System Broker which is available for
211
     *   services to use to access the database
212
     * @param transaction The transaction for the system services
213
     *
214
     * @throws BrokerPoolServiceException if any service causes an error during starting the system mode
215
     *
216
     * @throws IllegalStateException Thrown if there is an attempt to start a service
217
     * after any other service has entered the start pre-multi-user system mode.
218
     */
219
    void startSystemServices(final DBBroker systemBroker, final Txn transaction) throws BrokerPoolServiceException {
220
        states.process(ManagerEvent.ENTER_SYSTEM_MODE);
1✔
221

222
        for(final BrokerPoolService brokerPoolService : brokerPoolServices) {
1✔
223
            if(LOG.isTraceEnabled()) {
1!
224
                LOG.trace("Notifying service: {} of start system...", brokerPoolService.getClass().getSimpleName());
×
225
            }
226
            brokerPoolService.startSystem(systemBroker, transaction);
1✔
227
        }
228
    }
1✔
229

230
    /**
231
     * Starts any services which should be started directly after
232
     * the database finishes system mode operations, but before
233
     * entering multi-user mode
234
     *
235
     * At this point the broker pool is still in system (single user) mode
236
     * and not generally available for access, only a single
237
     * system broker is available.
238
     *
239
     * @param systemBroker The System Broker which is available for
240
     *   services to use to access the database
241
     * @param transaction The transaction for the pre-multi-user system services
242
     *
243
     * @throws BrokerPoolServiceException if any service causes an error during starting the pre-multi-user mode
244
     *
245
     * @throws IllegalStateException Thrown if there is an attempt to start pre-multi-user system a service
246
     * after any other service has entered multi-user.
247
     */
248
    void startPreMultiUserSystemServices(final DBBroker systemBroker, final Txn transaction) throws BrokerPoolServiceException {
249
        states.process(ManagerEvent.PREPARE_ENTER_MULTI_USER_MODE);
1✔
250

251
        for(final BrokerPoolService brokerPoolService : brokerPoolServices) {
1✔
252
            if(LOG.isTraceEnabled()) {
1!
253
                LOG.trace("Notifying service: {} of start pre-multi-user...", brokerPoolService.getClass().getSimpleName());
×
254
            }
255
            brokerPoolService.startPreMultiUserSystem(systemBroker, transaction);
1✔
256
        }
257
    }
1✔
258

259
    /**
260
     * Starts any services which should be started once the database
261
     * enters multi-user mode
262
     *
263
     * @param brokerPool The broker pool instance
264
     *
265
     * @throws BrokerPoolServiceException if any service causes an error during starting multi-user mode
266
     *
267
     * @throws IllegalStateException Thrown if there is an attempt to start multi-user a service
268
     * before we have completed pre-multi-user mode
269
     */
270
    void startMultiUserServices(final BrokerPool brokerPool) throws BrokerPoolServiceException {
271
        states.process(ManagerEvent.ENTER_MULTI_USER_MODE);
1✔
272

273
        for(final BrokerPoolService brokerPoolService : brokerPoolServices) {
1✔
274
            if(LOG.isTraceEnabled()) {
1!
275
                LOG.trace("Notifying service: {} of start multi-user...", brokerPoolService.getClass().getSimpleName());
×
276
            }
277
            brokerPoolService.startMultiUser(brokerPool);
1✔
278
        }
279
    }
1✔
280

281
    /**
282
     * Stops any services which should be stopped before the database
283
     * exits multi-user mode.
284
     *
285
     * @param brokerPool The broker pool instance
286
     *
287
     * @throws BrokerPoolServiceException if any service causes an error when stopping
288
     *
289
     * @throws IllegalStateException Thrown if there is an attempt to stop a service
290
     * before we have completed starting multi-user mode
291
     */
292
    void stopMultiUserServices(final BrokerPool brokerPool) throws BrokerPoolServicesManagerException {
293
        states.process(ManagerEvent.STOP_MULTI_USER_MODE);
1✔
294

295
        List<BrokerPoolServiceException> serviceExceptions = null;
1✔
296

297
        // we stop in the reverse order to starting up
298
        for(int i = brokerPoolServices.size() - 1; i >= 0; i--) {
1✔
299
            final BrokerPoolService brokerPoolService = brokerPoolServices.get(i);
1✔
300
            if(LOG.isTraceEnabled()) {
1!
301
                LOG.trace("Stopping multi-user service: {}...", brokerPoolService.getClass().getSimpleName());
×
302
            }
303

304
            try {
305
                brokerPoolService.stopMultiUser(brokerPool);
1✔
306
            } catch (final BrokerPoolServiceException e) {
1✔
307
                if(serviceExceptions == null) {
×
308
                    serviceExceptions = new ArrayList<>();
×
309
                }
310
                serviceExceptions.add(e);
×
311
            }
312
        }
313

314
        if(serviceExceptions != null) {
1!
315
            throw new BrokerPoolServicesManagerException(serviceExceptions);
×
316
        }
317
    }
1✔
318

319
    /**
320
     * Stops any services which should be stopped before the database
321
     * exits system mode.
322
     *
323
     * At this point the broker pool is back in system (single user) mode
324
     * and not generally available for access, only a single
325
     * system broker is available.
326
     *
327
     * @param systemBroker The System Broker which is available for
328
     *   services to use to access the database
329
     *
330
     * @throws BrokerPoolServiceException if any service causes an error when stopping
331
     *
332
     * @throws IllegalStateException Thrown if there is an attempt to stop a service
333
     * before we have completed stopping multi-user mode
334
     */
335
    void stopSystemServices(final DBBroker systemBroker) throws BrokerPoolServicesManagerException {
336
        states.process(ManagerEvent.STOP_SYSTEM_MODE);
1✔
337

338
        List<BrokerPoolServiceException> serviceExceptions = null;
1✔
339

340
        // we stop in the reverse order to starting up
341
        for(int i = brokerPoolServices.size() - 1; i >= 0; i--) {
1✔
342
            final BrokerPoolService brokerPoolService = brokerPoolServices.get(i);
1✔
343
            if(LOG.isTraceEnabled()) {
1!
344
                LOG.trace("Stopping system service: {}...", brokerPoolService.getClass().getSimpleName());
×
345
            }
346

347
            try {
348
                brokerPoolService.stopSystem(systemBroker);
1✔
349
            } catch (final BrokerPoolServiceException e) {
1✔
350
                if(serviceExceptions == null) {
×
351
                    serviceExceptions = new ArrayList<>();
×
352
                }
353
                serviceExceptions.add(e);
×
354
            }
355
        }
356

357
        if(serviceExceptions != null) {
1!
358
            throw new BrokerPoolServicesManagerException(serviceExceptions);
×
359
        }
360
    }
1✔
361

362
    /**
363
     * Shutdown any services which were previously configured.
364
     *
365
     * @throws IllegalStateException Thrown if there is an attempt to shutdown a service
366
     * before we have completed stopping services
367
     */
368
    void shutdown() {
369
        states.process(ManagerEvent.SHUTDOWN);
1✔
370

371
        // we shutdown in the reverse order to starting up
372
        for(int i = brokerPoolServices.size() - 1; i >= 0; i--) {
1✔
373
            final BrokerPoolService brokerPoolService = brokerPoolServices.get(i);
1✔
374
            if(LOG.isTraceEnabled()) {
1!
375
                LOG.trace("Shutting down service: {}...", brokerPoolService.getClass().getSimpleName());
×
376
            }
377
            brokerPoolService.shutdown();
1✔
378
        }
379

380
        brokerPoolServices.clear();
1✔
381
    }
1✔
382
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc