View Javadoc
1   /*
2    *    Copyright 2010-2023 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.migration;
17  
18  import static org.apache.ibatis.migration.options.OptionsParser.parse;
19  
20  import java.io.File;
21  import java.io.PrintStream;
22  import java.util.Date;
23  
24  import org.apache.ibatis.migration.commands.Command;
25  import org.apache.ibatis.migration.commands.Commands;
26  import org.apache.ibatis.migration.commands.InfoCommand;
27  import org.apache.ibatis.migration.commands.InitializeCommand;
28  import org.apache.ibatis.migration.options.Options;
29  import org.apache.ibatis.migration.options.SelectedOptions;
30  import org.apache.ibatis.migration.utils.Util;
31  
32  public class CommandLine {
33    // Leave this non static as it is messed with in this project tests
34    private final PrintStream console = System.out;
35    private final String[] args;
36  
37    public CommandLine(String[] args) {
38      this.args = args;
39    }
40  
41    public void execute() {
42      final SelectedOptions selectedOptions = parse(args);
43      if (selectedOptions.needsHelp()) {
44        printUsage();
45        return;
46      }
47      if (selectedOptions.getCommand() == null) {
48        console.printf("No command specified.%n");
49        printUsage();
50        return;
51      }
52      try {
53        Command command = Commands.resolveCommand(selectedOptions.getCommand(), selectedOptions);
54        if (command instanceof InitializeCommand || command instanceof InfoCommand
55            || validBasePath(selectedOptions.getPaths().getBasePath())) {
56          runCommand(command, selectedOptions);
57        }
58      } catch (Exception e) {
59        String errorMessage = e.getMessage();
60  
61        if (hasColor(selectedOptions)) {
62          console.printf(ConsoleColors.RED + "%nERROR: %s%n", errorMessage + ConsoleColors.RESET);
63        } else {
64          console.printf("%nERROR: %s%n", errorMessage);
65        }
66  
67        if (selectedOptions.isTrace()) {
68          e.printStackTrace();
69        }
70        console.close();
71        System.exit(1); // Issue 730
72      }
73      console.close();
74    }
75  
76    private void runCommand(Command command, SelectedOptions selectedOptions) {
77      console.printf("------------------------------------------------------------------------%n");
78      console.printf("-- MyBatis Migrations - %s%n", selectedOptions.getCommand());
79      console.printf("------------------------------------------------------------------------%n");
80  
81      long start = System.currentTimeMillis();
82      boolean exceptionCaught = false;
83  
84      try {
85        command.execute(selectedOptions.getParams());
86      } catch (Throwable t) {
87        exceptionCaught = true;
88        if (t instanceof MigrationException) {
89          throw (MigrationException) t;
90        }
91        throw new MigrationException(t);
92      } finally {
93        console.printf("------------------------------------------------------------------------%n");
94  
95        if (hasColor(selectedOptions)) {
96          console.printf("-- MyBatis Migrations %s%s%s%n", exceptionCaught ? ConsoleColors.RED : ConsoleColors.GREEN,
97              exceptionCaught ? "FAILURE" : "SUCCESS", ConsoleColors.RESET);
98        } else {
99          console.printf("-- MyBatis Migrations %s%n", exceptionCaught ? "FAILURE" : "SUCCESS");
100       }
101 
102       console.printf("-- Total time: %ss%n", (System.currentTimeMillis() - start) / 1000);
103       console.printf("-- Finished at: %s%n", new Date());
104       printMemoryUsage();
105       console.printf("------------------------------------------------------------------------%n");
106     }
107   }
108 
109   protected boolean hasColor(SelectedOptions selectedOptions) {
110     return selectedOptions.hasColor() || Util.getPropertyOptionAsBoolean(Options.COLOR.toString().toLowerCase());
111   }
112 
113   private void printMemoryUsage() {
114     final Runtime runtime = Runtime.getRuntime();
115     final int megaUnit = 1024 * 1024;
116     final long usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / megaUnit;
117     final long totalMemory = runtime.totalMemory() / megaUnit;
118 
119     console.printf("-- Final Memory: %sM/%sM%n", usedMemory, totalMemory);
120   }
121 
122   private boolean validBasePath(File basePath) {
123     final boolean validDirectory = basePath.exists() && basePath.isDirectory();
124 
125     if (!validDirectory) {
126       console.printf("Migrations path must be a directory: %s%n", basePath.getAbsolutePath());
127     }
128 
129     return validDirectory;
130   }
131 
132   private void printUsage() {
133     console.printf(
134         "%nUsage: migrate command [parameter] [--path=<directory>] [--env=<environment>] [--template=<path to custom template>]%n%n");
135     console.printf("--path=<directory>   Path to repository.  Default current working directory.%n");
136     console.printf("--env=<environment>  Environment to configure. Default environment is 'development'.%n");
137     console.printf("--template=<template>  Path to custom template for creating new sql scripts.%n");
138     console.printf("--force              Forces script to continue even if SQL errors are encountered.%n");
139     console.printf("--help               Displays this usage message.%n");
140     console.printf("--trace              Shows additional error details (if any).%n");
141     console.printf("--quiet              Suppresses output.%n");
142     console.printf("--color              Colorize output.%n");
143     console.printf("%n");
144     console.printf("Commands:%n");
145     console.printf("  info               Display build version informations.%n");
146     console.printf("  init               Creates (if necessary) and initializes a migration path.%n");
147     console.printf("  bootstrap          Runs the bootstrap SQL script (see scripts/bootstrap.sql for more).%n");
148     console.printf("  new <description>  Creates a new migration with the provided description.%n");
149     console.printf("  up [n]             Run unapplied migrations, ALL by default, or 'n' specified.%n");
150     console
151         .printf("  down [n]           Undoes migrations applied to the database. ONE by default or 'n' specified.%n");
152     console.printf("  version <version>  Migrates the database up or down to the specified version.%n");
153     console.printf("  pending            Force executes pending migrations out of order (not recommended).%n");
154     console.printf("  status             Prints the changelog from the database if the changelog table exists.%n");
155     console
156         .printf("  script <v1> <v2>   Generates a delta migration script from version v1 to v2 (undo if v1 > v2).%n");
157     console.printf("%n");
158     console.printf("  * Shortcuts are accepted by using the first few (unambiguous) letters of each command..%n");
159     console.printf("%n");
160   }
161 }