Showing 9 of 27 files from the diff.

@@ -32,7 +32,7 @@
Loading
32 32
33 33
import static com.google.common.base.Preconditions.checkNotNull;
34 34
import static io.spine.code.proto.ColumnOption.columnsOf;
35 -
import static io.spine.reflect.Methods.asHandle;
35 +
import static io.spine.reflect.Invokables.asHandle;
36 36
import static io.spine.util.Exceptions.illegalStateWithCauseOf;
37 37
import static io.spine.util.Exceptions.newIllegalStateException;
38 38

@@ -26,6 +26,7 @@
Loading
26 26
import io.spine.base.EventMessage;
27 27
import io.spine.base.Field;
28 28
import io.spine.base.FieldPath;
29 +
import io.spine.base.Production;
29 30
import io.spine.core.BoundedContext;
30 31
import io.spine.core.BoundedContextName;
31 32
import io.spine.logging.Logging;
@@ -106,7 +107,7 @@
Loading
106 107
    private BoundedContextName contextOf(Class<?> cls) {
107 108
        Model model = Model.inContextOf(cls);
108 109
        BoundedContextName name = model.contextName();
109 -
        if (Environment.instance().isProduction() && name.equals(assumingTests())) {
110 +
        if (Environment.instance().is(Production.class) && name.equals(assumingTests())) {
110 111
            _warn().log(
111 112
                    "The class `%s` belongs to the Bounded Context named `%s`," +
112 113
                    " which is used for testing. As such, it should not be used in production." +

@@ -25,6 +25,7 @@
Loading
25 25
import io.spine.server.ServerEnvironment;
26 26
import io.spine.server.delivery.memory.InMemoryShardedWorkRegistry;
27 27
import io.spine.server.storage.StorageFactory;
28 +
import io.spine.server.storage.memory.InMemoryStorageFactory;
28 29
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
29 30
30 31
import java.util.Optional;
@@ -209,6 +210,9 @@
Loading
209 210
     *
210 211
     * <p>If none set, the storage is initialized by the {@code StorageFactory} specific for
211 212
     * this {@code ServerEnvironment}.
213 +
     *
214 +
     * <p>If no {@code StorageFactory} is present in the {@code ServerEnvironment}, a new
215 +
     * {@code InMemoryStorageFactory} is used.
212 216
     */
213 217
    @CanIgnoreReturnValue
214 218
    public DeliveryBuilder setInboxStorage(InboxStorage inboxStorage) {
@@ -221,6 +225,9 @@
Loading
221 225
     *
222 226
     * <p>If none set, the storage is initialized by the {@code StorageFactory} specific for
223 227
     * this {@code ServerEnvironment}.
228 +
     *
229 +
     * <p>If no {@code StorageFactory} is present in the {@code ServerEnvironment}, a new
230 +
     * {@code InMemoryStorageFactory} is used.
224 231
     */
225 232
    @CanIgnoreReturnValue
226 233
    public DeliveryBuilder setCatchUpStorage(CatchUpStorage catchUpStorage) {
@@ -274,8 +281,7 @@
Loading
274 281
            deduplicationWindow = Duration.getDefaultInstance();
275 282
        }
276 283
277 -
        StorageFactory factory = ServerEnvironment.instance()
278 -
                                                  .storageFactory();
284 +
        StorageFactory factory = storageFactory();
279 285
        if (this.inboxStorage == null) {
280 286
            this.inboxStorage = factory.createInboxStorage(true);
281 287
        }
@@ -303,4 +309,10 @@
Loading
303 309
        Delivery delivery = new Delivery(this);
304 310
        return delivery;
305 311
    }
312 +
313 +
    private static StorageFactory storageFactory() {
314 +
        ServerEnvironment serverEnvironment = ServerEnvironment.instance();
315 +
        Optional<StorageFactory> currentStorageFactory = serverEnvironment.optionalStorageFactory();
316 +
        return currentStorageFactory.orElseGet(InMemoryStorageFactory::newInstance);
317 +
    }
306 318
}

@@ -24,6 +24,7 @@
Loading
24 24
import com.google.errorprone.annotations.CanIgnoreReturnValue;
25 25
import io.spine.annotation.Internal;
26 26
import io.spine.base.Environment;
27 +
import io.spine.base.Production;
27 28
28 29
/**
29 30
 * A configuration of features of a system context.
@@ -60,7 +61,7 @@
Loading
60 61
                .disableCommandLog()
61 62
                .enableAggregateQuerying()
62 63
                .forgetEvents();
63 -
        if (Environment.instance().isProduction()) {
64 +
        if (Environment.instance().is(Production.class)) {
64 65
            settings.enableParallelPosting();
65 66
        } else {
66 67
            settings.disableParallelPosting();

@@ -121,7 +121,7 @@
Loading
121 121
    void on(EntityRestored event) {
122 122
        Mirror.Builder builder = builder();
123 123
        builder.getLifecycleBuilder()
124 -
                .setDeleted(false);
124 +
               .setDeleted(false);
125 125
        builder.setId(id())
126 126
               .setVersion(event.getVersion());
127 127
        setDeleted(false);

@@ -0,0 +1,205 @@
Loading
1 +
/*
2 +
 * Copyright 2020, TeamDev. All rights reserved.
3 +
 *
4 +
 * Redistribution and use in source and/or binary forms, with or without
5 +
 * modification, must retain the above copyright notice and the following
6 +
 * disclaimer.
7 +
 *
8 +
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9 +
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10 +
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11 +
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12 +
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13 +
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14 +
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15 +
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16 +
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17 +
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18 +
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19 +
 */
20 +
21 +
package io.spine.server;
22 +
23 +
import com.google.common.annotations.VisibleForTesting;
24 +
import io.spine.annotation.Internal;
25 +
import io.spine.base.EnvironmentType;
26 +
27 +
import java.util.HashMap;
28 +
import java.util.Map;
29 +
import java.util.Optional;
30 +
import java.util.function.Supplier;
31 +
32 +
import static com.google.common.base.Preconditions.checkNotNull;
33 +
import static io.spine.util.Exceptions.newIllegalStateException;
34 +
35 +
/**
36 +
 * A mutable value that may differ between {@linkplain EnvironmentType environment types}.
37 +
 *
38 +
 * <p>For example:
39 +
 * <pre>
40 +
 * {@literal EnvSetting<StorageFactory>} storageFactory ={@literal new EnvSetting<>();}
41 +
 * storageFactory.use(InMemoryStorageFactory.newInstance(), Production.class);
42 +
 *
43 +
 * assertThat(storageFactory.optionalValue(Production.class)).isPresent();
44 +
 * assertThat(storageFactory.optionalValue(Tests.class)).isEmpty();
45 +
 * </pre>
46 +
 *
47 +
 * <h1>Fallback</h1>
48 +
 * <p>{@code EnvSetting} allows to configure a default value for an environment type. It is used
49 +
 * when the value for the environment hasn't been {@linkplain #use(V, Class) set explicitly}.
50 +
 * <pre>
51 +
 *      // Assuming the environment is `Tests`.
52 +
 *
53 +
 *      StorageFactory fallbackStorageFactory = createStorageFactory();
54 +
 *     {@literal EnvSetting<StorageFactory>} setting =
55 +
 *          {@literal new EnvSetting<>(Tests.class, () -> fallbackStorageFactory)};
56 +
 *
57 +
 *     // `use` was never called, so the fallback value is calculated and returned.
58 +
 *     assertThat(setting.optionalValue()).isPresent();
59 +
 *     assertThat(setting.value()).isSameInstanceAs(fallbackStorageFactory);
60 +
 * </pre>
61 +
 *
62 +
 * <p>Fallback values are calculated once on first {@linkplain #value(Class) access} for the
63 +
 * specified environment. Every subsequent access returns the cached value.
64 +
 *
65 +
 * <pre>
66 +
 *      // This `Supplier` is calculated only once.
67 +
 *     {@literal Supplier<StorageFactory>} fallbackStorage = InMemoryStorageFactory::newInstance;
68 +
 *
69 +
 *     {@literal EnvSetting<StorageFactory>} setting =
70 +
 *     {@literal new EnvSetting<>(Tests.class, fallbackStorage);}
71 +
 *
72 +
 *     // `Supplier` is calculated and cached.
73 +
 *     StorageFactory storageFactory = setting.value();
74 +
 *
75 +
 *     // Fallback value is taken from cache.
76 +
 *     StorageFactory theSameFactory = setting.value();
77 +
 * </pre>
78 +
 *
79 +
 * <p>{@code EnvSetting} values do not determine the environment themselves: it's up to the
80 +
 * caller to ask for the appropriate one.
81 +
 *
82 +
 * <p>This implementation does <b>not</b> perform any synchronization, thus, if different threads
83 +
 * {@linkplain #use(Object, Class) configure} and {@linkplain #value(Class) read the value},
84 +
 * no effort is made to ensure any consistency.
85 +
 *
86 +
 * @param <V>
87 +
 *         the type of value
88 +
 */
89 +
@Internal
90 +
final class EnvSetting<V> {
91 +
92 +
    private final Map<Class<? extends EnvironmentType>, V> environmentValues =
93 +
            new HashMap<>();
94 +
95 +
    private final Map<Class<? extends EnvironmentType>, Supplier<V>> fallbacks =
96 +
            new HashMap<>();
97 +
98 +
    /**
99 +
     * Creates a new instance without any fallback configuration.
100 +
     */
101 +
    EnvSetting() {
102 +
    }
103 +
104 +
    /**
105 +
     * Creates a new instance, configuring {@code fallback} to supply a default value.
106 +
     *
107 +
     * <p>If a value for {@code type} is not {@linkplain #use(Object, Class) set explicitly},
108 +
     * {@link #value(Class)} and {@link #optionalValue(Class)} return the {@code fallback} result.
109 +
     */
110 +
    EnvSetting(Class<? extends EnvironmentType> type, Supplier<V> fallback) {
111 +
        this.fallbacks.put(type, fallback);
112 +
    }
113 +
114 +
    /**
115 +
     * If the value for the specified environment has been configured, returns it. Returns an
116 +
     * empty {@code Optional} otherwise.
117 +
     */
118 +
    Optional<V> optionalValue(Class<? extends EnvironmentType> type) {
119 +
        Optional<V> result = valueFor(type);
120 +
        return result;
121 +
    }
122 +
123 +
    /**
124 +
     * If the value for the specified environment has been configured, runs the specified operation
125 +
     * against it. Does nothing otherwise.
126 +
     *
127 +
     * <p>If you wish to run an operation that doesn't throw, use {@code
128 +
     * optionalValue(type).ifPresent(operation)}.
129 +
     *
130 +
     * @param operation
131 +
     *         operation to run
132 +
     */
133 +
    void ifPresentForEnvironment(Class<? extends EnvironmentType> type,
134 +
                                 SettingOperation<V> operation) throws Exception {
135 +
        Optional<V> value = valueFor(type);
136 +
        if (value.isPresent()) {
137 +
            operation.accept(value.get());
138 +
        }
139 +
    }
140 +
141 +
    /**
142 +
     * If the value corresponding to the specified environment type is set, returns it.
143 +
     *
144 +
     * <p>If it is not set, returns a fallback value. If no fallback was configured, an
145 +
     * {@code IllegalStateException} is thrown.
146 +
     */
147 +
    V value(Class<? extends EnvironmentType> type) {
148 +
        checkNotNull(type);
149 +
        Optional<V> result = valueFor(type);
150 +
        return result.orElseThrow(
151 +
                () -> newIllegalStateException("Env setting for environment `%s` is unset.",
152 +
                                               type));
153 +
    }
154 +
155 +
    /**
156 +
     * Clears this setting, forgetting all of the configured values.
157 +
     *
158 +
     * <p>Cached default values are also cleared and will be recalculated using the {@code
159 +
     * Supplier} passed to the {@linkplain #EnvSetting(Class, Supplier) constructor}.
160 +
     */
161 +
    @VisibleForTesting
162 +
    void reset() {
163 +
        environmentValues.clear();
164 +
    }
165 +
166 +
    /**
167 +
     * Sets the specified value for the specified environment type.
168 +
     *
169 +
     * @param value
170 +
     *         value to assign to one of environments
171 +
     */
172 +
    void use(V value, Class<? extends EnvironmentType> type) {
173 +
        checkNotNull(value);
174 +
        checkNotNull(type);
175 +
        this.environmentValues.put(type, value);
176 +
    }
177 +
178 +
    private Optional<V> valueFor(Class<? extends EnvironmentType> type) {
179 +
        checkNotNull(type);
180 +
        V result = this.environmentValues.get(type);
181 +
        if (result == null) {
182 +
            Supplier<V> resultSupplier = this.fallbacks.get(type);
183 +
            if (resultSupplier == null) {
184 +
                return Optional.empty();
185 +
            }
186 +
            V newValue = resultSupplier.get();
187 +
            checkNotNull(newValue);
188 +
            this.use(newValue, type);
189 +
            return Optional.of(newValue);
190 +
        }
191 +
        return Optional.of(result);
192 +
    }
193 +
194 +
    /**
195 +
     * Represents an operation over the setting that returns no result and may finish with an error.
196 +
     *
197 +
     * @param <V>
198 +
     *         the type of setting to perform the operation over
199 +
     */
200 +
    interface SettingOperation<V> {
201 +
202 +
        /** Performs this operation on the specified value. */
203 +
        void accept(V value) throws Exception;
204 +
    }
205 +
}

@@ -320,6 +320,10 @@
Loading
320 320
     *
321 321
     * <p>Uses a {@linkplain UniformAcrossAllShards#singleShard() single-shard} splitting.
322 322
     *
323 +
     * <p>To construct a {@code Delivery} instance, a {@code StorageFactory} is needed.
324 +
     * If it was not configured in the {@code ServerEnvironment}, uses a new {@code
325 +
     * InMemoryStorageFactory}.
326 +
     *
323 327
     * @see #localAsync() to create an asynchronous version of the local {@code Delivery}
324 328
     */
325 329
    public static Delivery local() {
@@ -334,6 +338,10 @@
Loading
334 338
     * <p>The returned instance of {@code Delivery} is configured to use
335 339
     * {@linkplain UniformAcrossAllShards#singleShard() the single shard}.
336 340
     *
341 +
     * <p>To construct a {@code Delivery} instance, a {@code StorageFactory} is needed.
342 +
     * If it was not configured in the {@code ServerEnvironment}, a new {@code
343 +
     * InMemoryStorageFactory} used.
344 +
     *
337 345
     * @see #local() to create a syncrhonous version of the local {@code Delivery}
338 346
     */
339 347
    public static Delivery localAsync() {
@@ -459,8 +467,8 @@
Loading
459 467
        }
460 468
461 469
        int totalMessagesDelivered = stages.stream()
462 -
                                       .map(DeliveryStage::getMessagesDelivered)
463 -
                                       .reduce(0, Integer::sum);
470 +
                                           .map(DeliveryStage::getMessagesDelivered)
471 +
                                           .reduce(0, Integer::sum);
464 472
        return new RunResult(totalMessagesDelivered, !continueAllowed);
465 473
    }
466 474
@@ -491,9 +499,9 @@
Loading
491 499
    private ImmutableList<Station> conveyorStationsFor(Iterable<CatchUp> catchUpJobs,
492 500
                                                       DeliveryAction action) {
493 501
        return ImmutableList.of(
494 -
                    new CatchUpStation(action, catchUpJobs),
495 -
                    new LiveDeliveryStation(action, deduplicationWindow),
496 -
                    new CleanupStation()
502 +
                new CatchUpStation(action, catchUpJobs),
503 +
                new LiveDeliveryStation(action, deduplicationWindow),
504 +
                new CleanupStation()
497 505
        );
498 506
    }
499 507

@@ -21,28 +21,57 @@
Loading
21 21
package io.spine.server;
22 22
23 23
import com.google.common.annotations.VisibleForTesting;
24 +
import com.google.errorprone.annotations.CanIgnoreReturnValue;
25 +
import io.spine.annotation.Internal;
24 26
import io.spine.base.Environment;
27 +
import io.spine.base.EnvironmentType;
25 28
import io.spine.base.Identifier;
29 +
import io.spine.base.Production;
30 +
import io.spine.base.Tests;
26 31
import io.spine.server.commandbus.CommandScheduler;
27 32
import io.spine.server.commandbus.ExecutorCommandScheduler;
28 33
import io.spine.server.delivery.Delivery;
29 34
import io.spine.server.storage.StorageFactory;
30 35
import io.spine.server.storage.memory.InMemoryStorageFactory;
36 +
import io.spine.server.storage.system.SystemAwareStorageFactory;
31 37
import io.spine.server.trace.TracerFactory;
32 38
import io.spine.server.transport.TransportFactory;
33 39
import io.spine.server.transport.memory.InMemoryTransportFactory;
34 -
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
35 -
import org.checkerframework.checker.nullness.qual.Nullable;
36 40
37 41
import java.util.Optional;
38 42
import java.util.function.Supplier;
39 43
40 44
import static com.google.common.base.Preconditions.checkNotNull;
41 45
import static io.spine.server.storage.system.SystemAwareStorageFactory.wrap;
46 +
import static io.spine.util.Exceptions.newIllegalStateException;
42 47
43 48
/**
44 49
 * The server conditions and configuration under which the application operates.
50 +
 *
51 +
 * <h1>Configuration</h1>
52 +
 * <p>Some parts of the {@code ServerEnvironment} can be customized based on the {@code
53 +
 * EnvironmentType}. To do so, one of the overloads of the {@code use} method can be called.
54 +
 * Two environment types exist out of the box: {@link Tests} and {@link Production}. For example:
55 +
 * <pre>
56 +
 *     ServerEnvironment.use(productionStorageFactory, Production.class)
57 +
 *                      .use(testingStorageFactory, Tests.class)
58 +
 *                      .use(memoizingTracerFactory, Production.class)
59 +
 * </pre>
60 +
 * A custom environment type may also be used:
61 +
 * <pre>
62 +
 *     final class StagingEnvironment extends EnvironmentType {
63 +
 *         ...
64 +
 *     }
65 +
 *
66 +
 *     // Can also be
67 +
 *     // `ServerEnvironment.use(inMemoryStorageFactory, new Staging(stagingServiceDiscovery));`,
68 +
 *     // if `Staging` expects dependencies to its constructor.
69 +
 *     ServerEnvironment.use(inMemoryStorageFactory, Staging.class);
70 +
 * </pre>
71 +
 * If {@code Staging} is {@link Environment#type() enabled}, the specified value is going to be
72 +
 * returned on {@link #storageFactory()}.
45 73
 */
74 +
@SuppressWarnings("ClassWithTooManyMethods" /* there are some deprecated methods to be eliminated later. */)
46 75
public final class ServerEnvironment implements AutoCloseable {
47 76
48 77
    private static final ServerEnvironment INSTANCE = new ServerEnvironment();
@@ -68,34 +97,34 @@
Loading
68 97
     * The strategy of delivering the messages received by entity repositories
69 98
     * to the entity instances.
70 99
     *
71 -
     * <p>If not {@linkplain #configureDelivery(Delivery) configured by the end-user},
100 +
     * <p>If not {@linkplain #use(Delivery, EnvironmentType)}) configured by the end-user},
72 101
     * initialized with the {@linkplain Delivery#local() local} delivery by default.
102 +
     *
103 +
     * <p>Differs between {@linkplain EnvironmentType environment types}.
73 104
     */
74 -
    @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
75 -
      // No synchronization in `reset()` as it is test-only.
76 -
    private @MonotonicNonNull Delivery delivery;
77 -
78 -
    /**
79 -
     * The storage factory for the production mode of the application.
80 -
     */
81 -
    private @Nullable StorageFactory productionStorageFactory;
105 +
    private final EnvSetting<Delivery> delivery = new EnvSetting<>();
82 106
83 107
    /**
84 -
     * The storage factory for tests.
108 +
     * Storage factory. Differs between {@linkplain EnvironmentType environment types}.
85 109
     *
86 -
     * <p>If not configured, {@link InMemoryStorageFactory} will be used.
110 +
     * <p>Defaults to an {@code InMemoryStorageFactory} for tests.
87 111
     */
88 -
    private @Nullable StorageFactory storageFactoryForTests;
112 +
    private final EnvSetting<StorageFactory> storageFactory =
113 +
            new EnvSetting<>(Tests.class, () -> wrap(InMemoryStorageFactory.newInstance()));
89 114
90 115
    /**
91 -
     * The factory of {@code Tracer}s used in this environment.
116 +
     * Factory of {@code Tracer}s. Differs between {@linkplain EnvironmentType environment types}.
92 117
     */
93 -
    private @Nullable TracerFactory tracerFactory;
118 +
    private final EnvSetting<TracerFactory> tracerFactory = new EnvSetting<>();
94 119
95 120
    /**
96 -
     * The production factory for channel-based transport.
121 +
     * Factory for channel-based transport. Differs between {@linkplain EnvironmentType environment
122 +
     * types}.
123 +
     *
124 +
     * <p>Defaults to an {@code InMemoryTransportFactory} for tests.
97 125
     */
98 -
    private @Nullable TransportFactory transportFactory;
126 +
    private final EnvSetting<TransportFactory> transportFactory =
127 +
            new EnvSetting<>(Tests.class, InMemoryTransportFactory::newInstance);
99 128
100 129
    /**
101 130
     * Provides schedulers used by all {@code CommandBus} instances of this environment.
@@ -124,28 +153,35 @@
Loading
124 153
    }
125 154
126 155
    /**
127 -
     * Updates the delivery for this environment.
156 +
     * Updates the delivery for the current environment.
128 157
     *
129 158
     * <p>This method is most typically used upon an application start. It's very uncommon and
130 159
     * even dangerous to update the delivery mechanism later when the message delivery
131 160
     * process may have been already used by various {@code BoundedContext}s.
161 +
     *
162 +
     * @deprecated use {@link #use(Delivery, EnvironmentType)}
132 163
     */
164 +
    @Deprecated
133 165
    public synchronized void configureDelivery(Delivery delivery) {
134 166
        checkNotNull(delivery);
135 -
        this.delivery = delivery;
167 +
        use(delivery, this.delivery, environment().type());
136 168
    }
137 169
138 170
    /**
139 171
     * Returns the delivery mechanism specific to this environment.
140 172
     *
141 -
     * <p>Unless {@linkplain #configureDelivery(Delivery) updated manually}, returns
173 +
     * <p>Unless {@linkplain #use(Delivery, EnvironmentType) updated manually}, returns
142 174
     * a {@linkplain Delivery#local() local implementation} of {@code Delivery}.
143 175
     */
144 176
    public synchronized Delivery delivery() {
145 -
        if (delivery == null) {
146 -
            delivery = Delivery.local();
177 +
        Class<? extends EnvironmentType> currentEnv = environment().type();
178 +
        Optional<Delivery> currentDelivery = this.delivery.optionalValue(currentEnv);
179 +
        if (currentDelivery.isPresent()) {
180 +
            return currentDelivery.get();
147 181
        }
148 -
        return delivery;
182 +
        Delivery localDelivery = Delivery.local();
183 +
        this.delivery.use(localDelivery, currentEnv);
184 +
        return localDelivery;
149 185
    }
150 186
151 187
    /**
@@ -198,98 +234,193 @@
Loading
198 234
    }
199 235
200 236
    /**
201 -
     * Assigns {@code StorageFactory} for the production mode of the application.
237 +
     * Assigns the specified {@code TransportFactory} for the specified application environment.
238 +
     *
239 +
     * @return this instance of {@code ServerEnvironment}
240 +
     */
241 +
    @CanIgnoreReturnValue
242 +
    public ServerEnvironment use(StorageFactory factory, EnvironmentType envType) {
243 +
        checkNotNull(factory);
244 +
        SystemAwareStorageFactory wrapped = wrap(factory);
245 +
        use(wrapped, storageFactory, envType);
246 +
        return this;
247 +
    }
248 +
249 +
    /**
250 +
     * Assigns the specified {@code TransportFactory} for the specified application environment.
251 +
     *
252 +
     * @return this instance of {@code ServerEnvironment}
253 +
     */
254 +
    @CanIgnoreReturnValue
255 +
    public ServerEnvironment use(StorageFactory factory, Class<? extends EnvironmentType> type) {
256 +
        checkNotNull(factory);
257 +
        SystemAwareStorageFactory wrapped = wrap(factory);
258 +
        use(wrapped, storageFactory, type);
259 +
        return this;
260 +
    }
261 +
262 +
    /**
263 +
     * Assigns the specified {@code Delivery} for the selected environment.
202 264
     *
203 -
     * @see #configureStorageForTests(StorageFactory)
265 +
     * @return this instance of {@code ServerEnvironment}
204 266
     */
205 -
    public void configureStorage(StorageFactory productionStorageFactory) {
206 -
        checkNotNull(productionStorageFactory);
207 -
        this.productionStorageFactory = wrap(productionStorageFactory);
267 +
    @CanIgnoreReturnValue
268 +
    public ServerEnvironment use(Delivery delivery, EnvironmentType envType) {
269 +
        use(delivery, this.delivery, envType);
270 +
        return this;
208 271
    }
209 272
210 273
    /**
211 -
     * Assigns {@code StorageFactory} for tests.
274 +
     * Assigns the specified {@code Delivery} for the selected environment.
212 275
     *
213 -
     * @see #configureStorage(StorageFactory)
276 +
     * @return this instance of {@code ServerEnvironment}
214 277
     */
278 +
    @CanIgnoreReturnValue
279 +
    public ServerEnvironment use(Delivery delivery, Class<? extends EnvironmentType> envType) {
280 +
        use(delivery, this.delivery, envType);
281 +
        return this;
282 +
    }
283 +
284 +
    /**
285 +
     * Assigns {@code TracerFactory} for the specified application environment.
286 +
     *
287 +
     * @return this instance of {@code ServerEnvironment}
288 +
     */
289 +
    @CanIgnoreReturnValue
290 +
    public ServerEnvironment use(TracerFactory factory, EnvironmentType envType) {
291 +
        use(factory, tracerFactory, envType);
292 +
        return this;
293 +
    }
294 +
295 +
    /**
296 +
     * Assigns {@code TracerFactory} for the specified application environment.
297 +
     *
298 +
     * @return this instance of {@code ServerEnvironment}
299 +
     */
300 +
    @CanIgnoreReturnValue
301 +
    public ServerEnvironment use(TracerFactory factory, Class<? extends EnvironmentType> envType) {
302 +
        use(factory, tracerFactory, envType);
303 +
        return this;
304 +
    }
305 +
306 +
    /**
307 +
     * Configures the specified transport factory for the selected type of environment.
308 +
     *
309 +
     * @return this instance of {@code ServerEnvironment}
310 +
     */
311 +
    @CanIgnoreReturnValue
312 +
    public ServerEnvironment use(TransportFactory factory, EnvironmentType envType) {
313 +
        use(factory, transportFactory, envType);
314 +
        return this;
315 +
    }
316 +
317 +
    /**
318 +
     * Configures the specified transport factory for the selected type of environment.
319 +
     *
320 +
     * @return this instance of {@code ServerEnvironment}
321 +
     */
322 +
    @CanIgnoreReturnValue
323 +
    public ServerEnvironment use(TransportFactory factory, Class<? extends EnvironmentType> type) {
324 +
        use(factory, transportFactory, type);
325 +
        return this;
326 +
    }
327 +
328 +
    /**
329 +
     * Assigns the specified {@code StorageFactory} for tests.
330 +
     *
331 +
     * @deprecated use {@link #use(StorageFactory, Class)}, specifying {@code Tests.class)}
332 +
     */
333 +
    @Deprecated
215 334
    public void configureStorageForTests(StorageFactory factory) {
216 335
        checkNotNull(factory);
217 -
        this.storageFactoryForTests = wrap(factory);
336 +
        use(factory, Tests.class);
218 337
    }
219 338
220 339
    /**
221 -
     * Assigns {@code TracerFactory} to this server environment.
340 +
     * Assigns the specified {@code StorageFactory} for the {@link Production} application
341 +
     * environment.
342 +
     *
343 +
     * @deprecated use {@link #use(StorageFactory, Class)}, specifying the
344 +
     *         {@code Production.class} or any other environment type
222 345
     */
223 -
    public void configureTracing(TracerFactory tracerFactory) {
224 -
        this.tracerFactory = checkNotNull(tracerFactory);
346 +
    @Deprecated
347 +
    public void configureStorage(StorageFactory factory) {
348 +
        checkNotNull(factory);
349 +
        use(factory, Production.class);
225 350
    }
226 351
227 352
    /**
228 -
     * Obtains {@link TracerFactory} associated with this server environment.
353 +
     * Obtains the {@link TracerFactory} associated with the current environment, if it was set.
229 354
     */
230 355
    public Optional<TracerFactory> tracing() {
231 -
        return Optional.ofNullable(tracerFactory);
356 +
        Class<? extends EnvironmentType> currentType = environment().type();
357 +
        return this.tracerFactory.optionalValue(currentType);
232 358
    }
233 359
234 360
    /**
235 -
     * Obtains production {@code StorageFactory} previously associated with the environment.
361 +
     * Obtains the storage factory for the current environment.
236 362
     *
237 -
     * @return {@code StorageFactory} instance for the production storage
238 -
     * @throws NullPointerException
239 -
     *         if the production {@code StorageFactory} was not
240 -
     *         {@linkplain #configureStorage(StorageFactory) configured} prior to the call
363 +
     * <p>For tests, if the value was not set, defaults to a new {@code InMemoryStorageFactory}.
364 +
     *
365 +
     * <p>For other environments, if the value was not set, throws a {@code IllegalStateException}.
366 +
     *
367 +
     * @return {@code StorageFactory} instance for the storage for the current environment
368 +
     * @throws IllegalStateException
369 +
     *         if the value {@code StorageFactory} was not {@linkplain #use(StorageFactory, Class)}
370 +
     *         configured} prior to the call
241 371
     */
242 372
    public StorageFactory storageFactory() {
243 -
        if (environment().isTests()) {
244 -
            if (storageFactoryForTests == null) {
245 -
                configureStorageForTests(InMemoryStorageFactory.newInstance());
246 -
            }
247 -
            return storageFactoryForTests;
248 -
        }
249 -
        checkNotNull(productionStorageFactory,
250 -
                     "Production `%s` is not configured." +
251 -
                             " Please call `configureStorage()`.",
252 -
                     StorageFactory.class.getSimpleName()
253 -
        );
254 -
        return productionStorageFactory;
255 -
    }
256 -
257 -
    private static Environment environment() {
258 -
        return Environment.instance();
373 +
        Class<? extends EnvironmentType> type = environment().type();
374 +
        StorageFactory result = storageFactory
375 +
                .optionalValue(type)
376 +
                .orElseThrow(() -> {
377 +
                    String className = type.getSimpleName();
378 +
                    return newIllegalStateException(
379 +
                            "The storage factory for environment `%s` was not " +
380 +
                                    "configured. Please call `use(storage, %s);`.",
381 +
                            className, className);
382 +
                });
383 +
        return result;
259 384
    }
260 385
261 386
    /**
262 -
     * Assigns {@code TransportFactory} for the production mode of the application.
387 +
     * Returns a storage factory for the current environment, or an empty {@code Optional} if it
388 +
     * was not configured.
263 389
     */
264 -
    public void configureTransport(TransportFactory transportFactory) {
265 -
        this.transportFactory = checkNotNull(transportFactory);
390 +
    @Internal
391 +
    public Optional<StorageFactory> optionalStorageFactory() {
392 +
        Class<? extends EnvironmentType> type = environment().type();
393 +
        return storageFactory.optionalValue(type);
266 394
    }
267 395
268 396
    /**
269 -
     * Obtains {@code TransportFactory} associated with this server environment.
397 +
     * Obtains the transport factory for the current environment.
398 +
     *
399 +
     * <p>In the {@linkplain Tests testing environment}, if the factory was not assigned, assigns
400 +
     * a new {@code InMemoryTransportFactory} and returns it.
270 401
     *
271 -
     * <p>If the factory is not assigned in the Production mode, throws
272 -
     * {@code NullPointerException} with the instruction to call
273 -
     * {@link #configureTransport(TransportFactory)}.
402 +
     * <p>For all other environment types, throws an {@code IllegalStateException}.
274 403
     *
275 404
     * <p>If the factory is not assigned in the Tests mode, assigns the instance of
276 405
     * {@link InMemoryTransportFactory} and returns it.
277 406
     */
278 407
    public TransportFactory transportFactory() {
279 -
        boolean production = environment().isProduction();
280 -
        if (production) {
281 -
            checkNotNull(
282 -
                    transportFactory,
283 -
                    "`%s` is not assigned. Please call `configureTransport()`.",
284 -
                    TransportFactory.class.getName()
285 -
            );
286 -
            return transportFactory;
287 -
        }
408 +
        Class<? extends EnvironmentType> type = environment().type();
409 +
        TransportFactory result = transportFactory
410 +
                .optionalValue(type)
411 +
                .orElseThrow(() -> {
412 +
                    String environmentName = type.getSimpleName();
413 +
                    return newIllegalStateException(
414 +
                            "Transport factory is not assigned for the current environment `%s`. " +
415 +
                                    "Please call `use(transportFactory, %s);`.",
416 +
                            environmentName, environmentName);
417 +
                });
418 +
419 +
        return result;
420 +
    }
288 421
289 -
        if (transportFactory == null) {
290 -
            this.transportFactory = InMemoryTransportFactory.newInstance();
291 -
        }
292 -
        return transportFactory;
422 +
    private static Environment environment() {
423 +
        return Environment.instance();
293 424
    }
294 425
295 426
    /**
@@ -297,11 +428,12 @@
Loading
297 428
     */
298 429
    @VisibleForTesting
299 430
    public void reset() {
300 -
        this.transportFactory = null;
301 -
        this.tracerFactory = null;
302 -
        this.productionStorageFactory = null;
303 -
        this.storageFactoryForTests = null;
304 -
        this.delivery = Delivery.local();
431 +
        this.transportFactory.reset();
432 +
        this.tracerFactory.reset();
433 +
        this.storageFactory.reset();
434 +
        this.delivery.reset();
435 +
        Class<? extends EnvironmentType> currentEnv = environment().type();
436 +
        this.delivery.use(Delivery.local(), currentEnv);
305 437
        resetDeploymentType();
306 438
    }
307 439
@@ -310,14 +442,25 @@
Loading
310 442
     */
311 443
    @Override
312 444
    public void close() throws Exception {
313 -
        if (tracerFactory != null) {
314 -
            tracerFactory.close();
315 -
        }
316 -
        if (transportFactory != null) {
317 -
            transportFactory.close();
318 -
        }
319 -
        if (productionStorageFactory != null) {
320 -
            productionStorageFactory.close();
321 -
        }
445 +
        tracerFactory.ifPresentForEnvironment(Production.class, AutoCloseable::close);
446 +
        transportFactory.ifPresentForEnvironment(Production.class, AutoCloseable::close);
447 +
        storageFactory.ifPresentForEnvironment(Production.class, AutoCloseable::close);
448 +
    }
449 +
450 +
    private static <V> void use(V value, EnvSetting<V> setting, EnvironmentType type) {
451 +
        checkNotNull(value);
452 +
        checkNotNull(type);
453 +
        checkNotNull(setting);
454 +
        environment().register(type);
455 +
        setting.use(value, type.getClass());
456 +
    }
457 +
458 +
    private static <V> void use(V value,
459 +
                                EnvSetting<V> setting,
460 +
                                Class<? extends EnvironmentType> type) {
461 +
        checkNotNull(value);
462 +
        checkNotNull(type);
463 +
        checkNotNull(setting);
464 +
        setting.use(value, type);
322 465
    }
323 466
}

@@ -23,6 +23,7 @@
Loading
23 23
import com.google.common.annotations.VisibleForTesting;
24 24
import io.spine.annotation.Internal;
25 25
import io.spine.base.Environment;
26 +
import io.spine.base.Tests;
26 27
import io.spine.core.TenantId;
27 28
import io.spine.server.tenant.TenantAwareTestSupport;
28 29
import io.spine.server.tenant.TenantFunction;
@@ -34,14 +35,13 @@
Loading
34 35
/**
35 36
 * Abstract base for test suites that test tenant-aware functionality.
36 37
 *
37 -
 * <p>This class must be used only from {@linkplain Environment#isTests() test execution context}.
38 +
 * <p>This class must be used only from {@linkplain io.spine.base.Tests test execution environment}.
38 39
 */
39 40
@Internal
40 41
@VisibleForTesting
41 42
public abstract class TenantAwareTest {
42 43
43 -
    public static
44 -
    TenantIndex createTenantIndex(boolean multitenant) {
44 +
    public static TenantIndex createTenantIndex(boolean multitenant) {
45 45
        return multitenant
46 46
               ? TenantIndex.createDefault()
47 47
               : TenantIndex.singleTenant();
@@ -81,7 +81,7 @@
Loading
81 81
82 82
    private static void checkInTests() {
83 83
        checkState(Environment.instance()
84 -
                              .isTests());
84 +
                              .is(Tests.class));
85 85
    }
86 86
87 87
    private static TenantId currentTenant() {
Files Complexity Coverage
client/src/main/java/io/spine 86.94% 88.19%
core/src/main/java/io/spine 91.24% 94.77%
model 57.69% 52.02%
server/src/main/java/io/spine 88.87% 91.92%
testutil-client/src/main/java/io/spine/testing/client 94.74% 87.37%
testutil-core/src/main/java/io/spine/testing/core/given 92.86% 97.73%
testutil-server/src/main/java/io/spine/testing/server 90.04% 90.31%
Project Totals (606 files) 88.60% 91.06%
7760.1
TRAVIS_OS_NAME=linux
openjdk8=
7759.1
TRAVIS_OS_NAME=linux
openjdk8=
1
#
2
# See default configuration here: https://github.com/codecov/support/blob/master/codecov.yml
3
#
4
# For more options see: https://gist.github.com/stevepeak/53bee7b2c326b24a9b4a
5
#
6
# Codecov documentation is available here: https://codecov.io/docs
7

8
coverage:
9
  ignore:
10
  - generated/*
11
  - examples/*
12
  - test/*
13
  status:
14
    patch: false
15

16
comment:
17
  layout: "header, diff, changes, uncovered"
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading