001// Generated by delombok at Mon Oct 12 22:59:35 BST 2020
002/*
003 *  Licensed to the Apache Software Foundation (ASF) under one
004 *  or more contributor license agreements.  See the NOTICE file
005 *  distributed with this work for additional information
006 *  regarding copyright ownership.  The ASF licenses this file
007 *  to you under the Apache License, Version 2.0 (the
008 *  "License"); you may not use this file except in compliance
009 *  with the License.  You may obtain a copy of the License at
010 *
011 *        http://www.apache.org/licenses/LICENSE-2.0
012 *
013 *  Unless required by applicable law or agreed to in writing,
014 *  software distributed under the License is distributed on an
015 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 *  KIND, either express or implied.  See the License for the
017 *  specific language governing permissions and limitations
018 *  under the License.
019 */
020package org.apache.isis.extensions.commandreplay.secondary.clock;
021
022import java.sql.Timestamp;
023import java.util.function.Supplier;
024import javax.annotation.PostConstruct;
025import javax.inject.Named;
026import org.springframework.core.annotation.Order;
027import org.springframework.stereotype.Service;
028import org.apache.isis.applib.annotation.OrderPrecedence;
029import org.apache.isis.applib.clock.Clock;
030import org.apache.isis.core.config.IsisConfiguration;
031import org.apache.isis.testing.fixtures.applib.clock.TickingFixtureClock;
032
033/**
034 * Only enabled for the <tt>secondary</tt> profile, where it sets up the
035
036 * framework to use {@link TickingFixtureClock} so that time can be changed
037
038 * dynamically when running.
039
040 *
041
042 * <p>
043
044 *     As an additional safeguard, if the configuration keys to access the
045
046 *     primary are not provided, then the service will not initialize.
047
048 * </p>
049
050 *
051
052 * <p>
053
054 *     IMPORTANT: the methods provided by this service are not thread-safe,
055
056 *     because the clock is a globally-scoped singleton rather than a
057
058 *     thread-local.  These methods should therefore only be used in single-user
059
060 *     systems, eg a replay secondary.
061
062 * </p>
063 */
064@Service
065@Named("isisExtensionsCommandReplaySecondary.TickingClockService")
066@Order(OrderPrecedence.MIDPOINT)
067public class TickingClockService {
068    @java.lang.SuppressWarnings("all")
069    private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(TickingClockService.class);
070    final IsisConfiguration isisConfiguration;
071
072    @PostConstruct
073    public void init() {
074        final java.util.Optional<java.lang.String> baseUrl = isisConfiguration.getExtensions().getCommandReplay().getPrimaryAccess().getBaseUrlRestful();
075        final java.util.Optional<java.lang.String> user = isisConfiguration.getExtensions().getCommandReplay().getPrimaryAccess().getUser();
076        final java.util.Optional<java.lang.String> password = isisConfiguration.getExtensions().getCommandReplay().getPrimaryAccess().getPassword();
077        if (!baseUrl.isPresent() || !user.isPresent() || !password.isPresent()) {
078            log.warn("init() - skipping, one or more \'isis.extensions.command-replay.primary\' configuration properties has not been set");
079            return;
080        }
081        log.info("init() - replacing existing clock with TickingFixtureClock");
082        TickingFixtureClock.replaceExisting();
083    }
084
085    public boolean isInitialized() {
086        return Clock.getInstance() instanceof TickingFixtureClock;
087    }
088
089    /**
090     * Executes the runnable, setting the clock to be the specified time
091
092     * beforehand (and reinstating it to its original time afterwards).
093
094     *
095
096     * <p>
097
098     *     IMPORTANT: this method is not thread-safe, because the clock is a
099
100     *     globally-scoped singleton rather than a thread-local.  This method
101
102     *     should therefore only be used in single-user systems, eg a replay
103
104     *     secondary.
105
106     * </p>
107     */
108    public void at(Timestamp timestamp, Runnable runnable) {
109        ensureInitialized();
110        final org.apache.isis.testing.fixtures.applib.clock.TickingFixtureClock tickingFixtureClock = (TickingFixtureClock) TickingFixtureClock.getInstance();
111        final long previous = TickingFixtureClock.getEpochMillis();
112        final long wallTime0 = System.currentTimeMillis();
113        try {
114            tickingFixtureClock.setTime(timestamp);
115            runnable.run();
116        } finally {
117            final long wallTime1 = System.currentTimeMillis();
118            tickingFixtureClock.setTime(previous + wallTime1 - wallTime0);
119        }
120    }
121
122    /**
123     * Executes the callable, setting the clock to be the specified time
124
125     * beforehand (and reinstating it to its original time afterwards).
126
127     *
128
129     * <p>
130
131     *     IMPORTANT: this method is not thread-safe, because the clock is a
132
133     *     globally-scoped singleton rather than a thread-local.  This method
134
135     *     should therefore only be used in single-user systems, eg a replay
136
137     *     secondary.
138
139     * </p>
140     */
141    public <T> T at(Timestamp timestamp, Supplier<T> supplier) {
142        ensureInitialized();
143        final org.apache.isis.testing.fixtures.applib.clock.TickingFixtureClock tickingFixtureClock = (TickingFixtureClock) TickingFixtureClock.getInstance();
144        final long previous = TickingFixtureClock.getEpochMillis();
145        final long wallTime0 = System.currentTimeMillis();
146        try {
147            tickingFixtureClock.setTime(timestamp);
148            return supplier.get();
149        } finally {
150            final long wallTime1 = System.currentTimeMillis();
151            tickingFixtureClock.setTime(previous + wallTime1 - wallTime0);
152        }
153    }
154
155    private void ensureInitialized() {
156        if (!isInitialized()) {
157            throw new IllegalStateException("Not initialized.  Make sure that the application is configured as a replay secondary by configuring the \'isis.extensions.command-replay.primary\' configuration properties.");
158        }
159    }
160
161    @java.lang.SuppressWarnings("all")
162    public TickingClockService(final IsisConfiguration isisConfiguration) {
163        this.isisConfiguration = isisConfiguration;
164    }
165}