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
021/**
022 * Migration Service.
023 *
024 * @since 9.3
025 */
026public interface MigrationService {
027
028    /**
029     * Interface for the implementation of a migrator.
030     *
031     * @since 9.3
032     */
033    interface Migrator {
034
035        /**
036         * Probes the current state of a migration by analyzing persistent data.
037         * <p>
038         * Assumes no migration step is currently running.
039         * <p>
040         * THIS METHOD MAY TAKE A WHILE as it needs to get information from persistent storage.
041         *
042         * @return the probed state, or {@code null} if it cannot be determined
043         * @since 10.3
044         */
045        default String probeState() {
046            return null;
047        }
048
049        /**
050         * Runs a migration step.
051         * <p>
052         * This method should periodically check for {@link MigrationContext#isShutdownRequested} and
053         * {@link Thread#isInterrupted} and return if {@code true}.
054         *
055         * @param step the migration step to run
056         * @param migrationContext the migration context.
057         */
058        void run(String step, MigrationContext migrationContext);
059
060        /**
061         * Allows notification of status change for a running step or new state.
062         *
063         * @since 10.3
064         */
065        void notifyStatusChange();
066    }
067
068    /**
069     * Interface for a migration context, passed to the {@link Migrator}.
070     *
071     * @since 9.3
072     */
073    interface MigrationContext {
074
075        /**
076         * Notifies the migration context of the current progress.
077         *
078         * @param message an informative message about what is being migrated
079         * @param num the current number of things migrated
080         * @param total the total number of things to migrate, or {@code -1} if unknown
081         */
082        void reportProgress(String message, long num, long total);
083
084        /**
085         * Requests a shutdown. Called internally by the migration service when the server shuts down.
086         */
087        void requestShutdown();
088
089        /**
090         * Checks if shutdown has been requested.
091         * <p>
092         * This should be checked periodically by the migrator, and when {@code true} the migrator should return as soon
093         * as possible, even if its work is not complete.
094         * <p>
095         * This is a "nice" version of thread interruption, which will follow a short while later, and should also be
096         * checked by the migrator.
097         *
098         * @return {@code true} if migration should be stopped as soon as possible
099         */
100        boolean isShutdownRequested();
101    }
102
103    /**
104     * The status of a migration.
105     * <p>
106     * A migration is either running or not. When not running, it just has a state.
107     * <p>
108     * When running, it has a step, start time, and progress information (message, num, total, last ping time).
109     *
110     * @since 9.3
111     */
112    public class MigrationStatus {
113
114        protected final String state;
115
116        protected final String step;
117
118        protected final long startTime;
119
120        protected final long pingTime;
121
122        protected final String progressMessage;
123
124        protected final long progressNum;
125
126        protected final long progressTotal;
127
128        public MigrationStatus(String state) {
129            this.state = state;
130            step = null;
131            startTime = 0;
132            pingTime = 0;
133            progressMessage = null;
134            progressNum = 0;
135            progressTotal = 0;
136        }
137
138        public MigrationStatus(String step, long startTime, long pingTime, String progressMessage, long progressNum,
139                long progressTotal) {
140            state = null;
141            this.step = step;
142            this.startTime = startTime;
143            this.pingTime = pingTime;
144            this.progressMessage = progressMessage;
145            this.progressNum = progressNum;
146            this.progressTotal = progressTotal;
147        }
148
149        /**
150         * Checks whether the migration is running.
151         *
152         * @return {@code true} if a migration is running, or {@code false} otherwise
153         */
154        public boolean isRunning() {
155            return state == null;
156        }
157
158        /**
159         * Gets the state of the migration, if it's not running.
160         */
161        public String getState() {
162            return state;
163        }
164
165        /**
166         * Gets the step of the migration, if it's running.
167         */
168        public String getStep() {
169            return step;
170        }
171
172        /**
173         * Gets the start time of the migration, if it's running.
174         */
175        public long getStartTime() {
176            return startTime;
177        }
178
179        /**
180         * Gets the ping time of the migration, if it's running.
181         */
182        public long getPingTime() {
183            return pingTime;
184        }
185
186        /**
187         * Gets the progress message of the migration, if it's running.
188         */
189        public String getProgressMessage() {
190            return progressMessage;
191        }
192
193        /**
194         * Gets the progress "num" of the migration, if it's running.
195         */
196        public long getProgressNum() {
197            return progressNum;
198        }
199
200        /**
201         * Gets the progress "total" of the migration, if it's running.
202         */
203        public long getProgressTotal() {
204            return progressTotal;
205        }
206    }
207
208    /**
209     * Gets the current status for a migration.
210     *
211     * @param id the migration id
212     * @return the status, or {@code null} if the migration is unknown
213     */
214    MigrationStatus getStatus(String id);
215
216    /**
217     * Probes the current state of a migration by analyzing persistent data, and sets it as the new current state.
218     * <p>
219     * THIS METHOD MAY TAKE A WHILE as it needs to get information from persistent storage.
220     *
221     * @param id the migration id
222     * @return the new state, or {@code null} if it cannot be determined
223     * @since 10.3
224     */
225    String probeAndSetState(String id);
226
227    /**
228     * Runs a migration step for a migration.
229     * <p>
230     * This launches the migration asynchronously. The status of the migration can be checked with {@link #getStatus}.
231     *
232     * @param id the migration id
233     * @param step the step id
234     */
235    void runStep(String id, String step);
236
237}