001/*
002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019package org.nuxeo.runtime.migration;
020
021import java.util.List;
022
023/**
024 * Migration Service.
025 *
026 * @since 9.3
027 */
028public interface MigrationService {
029
030    /**
031     * Interface for the implementation of a migrator.
032     *
033     * @since 9.3
034     */
035    interface Migrator {
036
037        /**
038         * Probes the current state of a migration by analyzing persistent data.
039         * <p>
040         * Assumes no migration step is currently running.
041         * <p>
042         * THIS METHOD MAY TAKE A WHILE as it needs to get information from persistent storage.
043         *
044         * @return the probed state, or {@code null} if it cannot be determined
045         * @since 10.3
046         */
047        default String probeState() {
048            return null;
049        }
050
051        /**
052         * Runs a migration step.
053         * <p>
054         * This method should periodically check for {@link MigrationContext#isShutdownRequested} and
055         * {@link Thread#isInterrupted()} and return if {@code true}.
056         *
057         * @param step the migration step to run
058         * @param migrationContext the migration context.
059         */
060        void run(String step, MigrationContext migrationContext);
061
062        /**
063         * Allows notification of status change for a running step or new state.
064         *
065         * @since 10.3
066         */
067        void notifyStatusChange();
068    }
069
070    /**
071     * Interface for a migration context, passed to the {@link Migrator}.
072     *
073     * @since 9.3
074     */
075    interface MigrationContext {
076
077        /**
078         * Notifies the migration context of the current progress.
079         *
080         * @param message an informative message about what is being migrated
081         * @param num the current number of things migrated
082         * @param total the total number of things to migrate, or {@code -1} if unknown
083         */
084        void reportProgress(String message, long num, long total);
085
086        /**
087         * Requests a shutdown. Called internally by the migration service when the server shuts down.
088         */
089        void requestShutdown();
090
091        /**
092         * Checks if shutdown has been requested.
093         * <p>
094         * This should be checked periodically by the migrator, and when {@code true} the migrator should return as soon
095         * as possible, even if its work is not complete.
096         * <p>
097         * This is a "nice" version of thread interruption, which will follow a short while later, and should also be
098         * checked by the migrator.
099         *
100         * @return {@code true} if migration should be stopped as soon as possible
101         */
102        boolean isShutdownRequested();
103    }
104
105    /**
106     * The status of a migration.
107     * <p>
108     * A migration is either running or not. When not running, it just has a state.
109     * <p>
110     * When running, it has a step, start time, and progress information (message, num, total, last ping time).
111     *
112     * @since 9.3
113     */
114    class MigrationStatus {
115
116        protected final String state;
117
118        protected final String step;
119
120        protected final long startTime;
121
122        protected final long pingTime;
123
124        protected final String progressMessage;
125
126        protected final long progressNum;
127
128        protected final long progressTotal;
129
130        public MigrationStatus(String state) {
131            this.state = state;
132            step = null;
133            startTime = 0;
134            pingTime = 0;
135            progressMessage = null;
136            progressNum = 0;
137            progressTotal = 0;
138        }
139
140        public MigrationStatus(String step, long startTime, long pingTime, String progressMessage, long progressNum,
141                long progressTotal) {
142            state = null;
143            this.step = step;
144            this.startTime = startTime;
145            this.pingTime = pingTime;
146            this.progressMessage = progressMessage;
147            this.progressNum = progressNum;
148            this.progressTotal = progressTotal;
149        }
150
151        /**
152         * Checks whether the migration is running.
153         *
154         * @return {@code true} if a migration is running, or {@code false} otherwise
155         */
156        public boolean isRunning() {
157            return state == null;
158        }
159
160        /**
161         * Gets the state of the migration, if it's not running.
162         */
163        public String getState() {
164            return state;
165        }
166
167        /**
168         * Gets the step of the migration, if it's running.
169         */
170        public String getStep() {
171            return step;
172        }
173
174        /**
175         * Gets the start time of the migration, if it's running.
176         */
177        public long getStartTime() {
178            return startTime;
179        }
180
181        /**
182         * Gets the ping time of the migration, if it's running.
183         */
184        public long getPingTime() {
185            return pingTime;
186        }
187
188        /**
189         * Gets the progress message of the migration, if it's running.
190         */
191        public String getProgressMessage() {
192            return progressMessage;
193        }
194
195        /**
196         * Gets the progress "num" of the migration, if it's running.
197         */
198        public long getProgressNum() {
199            return progressNum;
200        }
201
202        /**
203         * Gets the progress "total" of the migration, if it's running.
204         */
205        public long getProgressTotal() {
206            return progressTotal;
207        }
208    }
209
210    /**
211     * Gets a migration object.
212     *
213     * @param id the migration id
214     * @return the migration, or {@code null} if the migration is unknown
215     * @since 11.2
216     */
217    Migration getMigration(String id);
218
219    /**
220     * Gets all the migration objects.
221     *
222     * @return a list of all migrations
223     * @since 11.2
224     */
225    List<Migration> getMigrations();
226
227    /**
228     * Gets the current status for a migration.
229     *
230     * @param id the migration id
231     * @return the status, or {@code null} if the migration is unknown
232     */
233    MigrationStatus getStatus(String id);
234
235    /**
236     * Probes the current state of a migration by analyzing persistent data, and sets it as the new current state.
237     * <p>
238     * THIS METHOD MAY TAKE A WHILE as it needs to get information from persistent storage.
239     *
240     * @param id the migration id
241     * @return the new state, or {@code null} if it cannot be determined
242     * @since 10.3
243     */
244    String probeAndSetState(String id);
245
246    /**
247     * Probes the migration state and runs a migration.
248     * <p>
249     * THIS METHOD MAY TAKE A WHILE as it may call {@link #probeAndSetState(String)}.
250     *
251     * @param id the migration id
252     * @since 11.2
253     */
254    void probeAndRun(String id);
255
256    /**
257     * Runs a migration step for a migration.
258     * <p>
259     * This launches the migration asynchronously. The status of the migration can be checked with {@link #getStatus}.
260     *
261     * @param id the migration id
262     * @param step the step id
263     */
264    void runStep(String id, String step);
265
266}