1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.jumpmind.symmetric;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.FileWriter;
27 import java.io.IOException;
28 import java.io.InputStreamReader;
29 import java.net.MalformedURLException;
30 import java.nio.charset.Charset;
31 import java.sql.Connection;
32
33 import org.apache.commons.cli.CommandLine;
34 import org.apache.commons.cli.CommandLineParser;
35 import org.apache.commons.cli.HelpFormatter;
36 import org.apache.commons.cli.Options;
37 import org.apache.commons.cli.ParseException;
38 import org.apache.commons.cli.PosixParser;
39 import org.apache.commons.dbcp.BasicDataSource;
40 import org.apache.commons.lang.exception.ExceptionUtils;
41 import org.apache.ddlutils.Platform;
42 import org.apache.ddlutils.io.DatabaseIO;
43 import org.apache.ddlutils.model.Database;
44 import org.jumpmind.symmetric.common.Constants;
45 import org.jumpmind.symmetric.db.IDbDialect;
46 import org.jumpmind.symmetric.db.SqlScript;
47 import org.jumpmind.symmetric.service.IBootstrapService;
48 import org.jumpmind.symmetric.service.IDataExtractorService;
49 import org.jumpmind.symmetric.service.IDataLoaderService;
50 import org.jumpmind.symmetric.service.IDataService;
51 import org.jumpmind.symmetric.service.IPurgeService;
52 import org.jumpmind.symmetric.service.IRegistrationService;
53 import org.jumpmind.symmetric.transport.IOutgoingTransport;
54 import org.jumpmind.symmetric.transport.internal.InternalOutgoingTransport;
55 import org.springframework.context.ApplicationContext;
56 import org.springframework.context.support.ClassPathXmlApplicationContext;
57
58 /***
59 * Run symmetric utilities and/or launch an embedded version of Symmetric. If
60 * you run this program without any arguments 'help' will print out.
61 */
62 public class SymmetricLauncher {
63
64 private static final String OPTION_DUMP_BATCH = "dump-batch";
65
66 private static final String OPTION_OPEN_REGISTRATION = "open-registration";
67
68 private static final String OPTION_RELOAD_NODE = "reload-node";
69
70 private static final String OPTION_AUTO_CREATE = "auto-create";
71
72 private static final String OPTION_PORT_SERVER = "port";
73
74 private static final String OPTION_DDL_GEN = "generate-config-dll";
75
76 private static final String OPTION_PURGE = "purge";
77
78 private static final String OPTION_RUN_DDL_XML = "run-ddl";
79
80 private static final String OPTION_RUN_SQL = "run-sql";
81
82 private static final String OPTION_PROPERTIES_GEN = "generate-default-properties";
83
84 private static final String OPTION_PROPERTIES_FILE = "properties";
85
86 private static final String OPTION_START_SERVER = "server";
87
88 private static final String OPTION_LOAD_BATCH = "load-batch";
89
90 private static final String OPTION_SKIP_DB_VALIDATION = "skip-db-validate";
91
92 public static void main(String[] args) throws Exception {
93
94 CommandLineParser parser = new PosixParser();
95 Options options = buildOptions();
96 try {
97 CommandLine line = parser.parse(options, args);
98
99 int serverPort = 31415;
100
101 if (line.hasOption(OPTION_PORT_SERVER)) {
102 serverPort = new Integer(line
103 .getOptionValue(OPTION_PORT_SERVER));
104 }
105
106 if (line.hasOption(OPTION_PROPERTIES_GEN)) {
107 generateDefaultProperties(line
108 .getOptionValue(OPTION_PROPERTIES_GEN));
109 return;
110 }
111
112
113 if (line.hasOption(OPTION_PROPERTIES_FILE)) {
114 System.setProperty(Constants.OVERRIDE_PROPERTIES_FILE_1,
115 "file:" + line.getOptionValue(OPTION_PROPERTIES_FILE));
116 if (!new File(line.getOptionValue(OPTION_PROPERTIES_FILE))
117 .exists()) {
118 throw new ParseException(
119 "Could not find the properties file specified: "
120 + line
121 .getOptionValue(OPTION_PROPERTIES_FILE));
122 }
123
124 }
125
126 if (line.hasOption(OPTION_DDL_GEN)) {
127 generateDDL(new SymmetricEngine(), line
128 .getOptionValue(OPTION_DDL_GEN));
129 return;
130 }
131
132 if (line.hasOption(OPTION_PURGE)) {
133 ((IPurgeService) new SymmetricEngine().getApplicationContext()
134 .getBean(Constants.PURGE_SERVICE)).purge();
135 return;
136 }
137
138 if (line.hasOption(OPTION_OPEN_REGISTRATION)) {
139 String arg = line.getOptionValue(OPTION_OPEN_REGISTRATION);
140 openRegistration(new SymmetricEngine(), arg);
141 System.out.println("Opened Registration for " + arg);
142 return;
143 }
144
145 if (line.hasOption(OPTION_RELOAD_NODE)) {
146 String arg = line.getOptionValue(OPTION_RELOAD_NODE);
147 String message = reloadNode(new SymmetricEngine(), arg);
148 System.out.println(message);
149 return;
150 }
151
152 if (line.hasOption(OPTION_DUMP_BATCH)) {
153 String arg = line.getOptionValue(OPTION_DUMP_BATCH);
154 dumpBatch(new SymmetricEngine(), arg);
155 return;
156 }
157
158 if (line.hasOption(OPTION_AUTO_CREATE)) {
159 autoCreateDatabase(new SymmetricEngine());
160 return;
161 }
162
163 if (line.hasOption(OPTION_RUN_DDL_XML)) {
164 runDdlXml(new SymmetricEngine(), line
165 .getOptionValue(OPTION_RUN_DDL_XML));
166 return;
167 }
168
169 if (line.hasOption(OPTION_RUN_SQL)) {
170 runSql(new SymmetricEngine(), line
171 .getOptionValue(OPTION_RUN_SQL));
172 return;
173 }
174
175 if (line.hasOption(OPTION_LOAD_BATCH)) {
176 loadBatch(new SymmetricEngine(), line
177 .getOptionValue(OPTION_LOAD_BATCH));
178 }
179
180 if (line.hasOption(OPTION_START_SERVER)) {
181 if (!line.hasOption(OPTION_SKIP_DB_VALIDATION)) {
182 testConnection();
183 }
184 new SymmetricWebServer().start(serverPort);
185 return;
186 }
187
188 printHelp(options);
189
190 } catch (ParseException exp) {
191 System.err.println(exp.getMessage());
192 printHelp(options);
193 } catch (Exception ex) {
194 System.err
195 .println("-----------------------------------------------------------------------------------------------");
196 System.err
197 .println(" An exception occurred. Please see the following for details: ");
198 System.err
199 .println("-----------------------------------------------------------------------------------------------");
200
201 ExceptionUtils.printRootCauseStackTrace(ex, System.err);
202 System.err
203 .println("-----------------------------------------------------------------------------------------------");
204 printHelp(options);
205 }
206 }
207
208 private static void printHelp(Options options) {
209 new HelpFormatter().printHelp("sym", options);
210 }
211
212 private static void testConnection() throws Exception {
213 ApplicationContext ctx = new ClassPathXmlApplicationContext(
214 new String[] { "classpath:/symmetric-properties.xml",
215 "classpath:/symmetric-database.xml" });
216 BasicDataSource ds = (BasicDataSource) ctx
217 .getBean(Constants.DATA_SOURCE);
218 Connection c = ds.getConnection();
219 c.close();
220 ds.close();
221 }
222
223 private static Options buildOptions() {
224 Options options = new Options();
225 options.addOption("S", OPTION_START_SERVER, false,
226 "Start an embedded instance of symmetric.");
227 options
228 .addOption("P", OPTION_PORT_SERVER, true,
229 "Optionally pass in the HTTP port number to use for the server instance.");
230 options
231 .addOption(
232 "c",
233 OPTION_DDL_GEN,
234 true,
235 "Output the DDL to create the symmetric tables. Takes an argument of the name of the file to write the ddl to.");
236 options
237 .addOption(
238 "p",
239 OPTION_PROPERTIES_FILE,
240 true,
241 "Takes an argument with the path to the properties file that will drive symmetric. If this is not provided, symmetric will use defaults, then override with the first symmetric.properties in your classpath, then override with symmetric.properties values in your user.home directory.");
242 options
243 .addOption("X", OPTION_PURGE, false,
244 "Will simply run the purge process against the currently configured database.");
245 options
246 .addOption(
247 "g",
248 OPTION_PROPERTIES_GEN,
249 true,
250 "Takes an argument with the path to a file which all the default overrideable properties will be written.");
251 options
252 .addOption(
253 "r",
254 OPTION_RUN_DDL_XML,
255 true,
256 "Takes an argument of a DdlUtils xml file and applies it to the database configured in your symmetric properties file.");
257 options
258 .addOption(
259 "s",
260 OPTION_RUN_SQL,
261 true,
262 "Takes an argument of a .sql file and runs it against the database configured in your symmetric properties file.");
263
264 options
265 .addOption("a", OPTION_AUTO_CREATE, false,
266 "Attempts to create the symmetric tables in the configured database.");
267 options
268 .addOption(
269 "R",
270 OPTION_OPEN_REGISTRATION,
271 true,
272 "Open registration for the passed in node group and external id. Takes an argument of {groupId},{externalId}.");
273 options
274 .addOption("l", OPTION_RELOAD_NODE, true,
275 "Send an initial load of data to reload the passed in node id.");
276 options
277 .addOption(
278 "d",
279 OPTION_DUMP_BATCH,
280 true,
281 "Print the contents of a batch out to the console. Takes the batch id as an argument.");
282 options.addOption("b", OPTION_LOAD_BATCH, true,
283 "Load the CSV contents of the specfied file.");
284 options
285 .addOption(
286 "i",
287 OPTION_SKIP_DB_VALIDATION,
288 false,
289 "Don't test to see if the database connection is valid before starting the server. Note that if the connection is invalid, then the server will continually try to connect if this is set.");
290 return options;
291 }
292
293 private static void dumpBatch(SymmetricEngine engine, String batchId)
294 throws Exception {
295 IDataExtractorService dataExtractorService = (IDataExtractorService) engine
296 .getApplicationContext().getBean(
297 Constants.DATAEXTRACTOR_SERVICE);
298 IOutgoingTransport transport = new InternalOutgoingTransport(System.out);
299 dataExtractorService.extractBatchRange(transport, batchId, batchId);
300 transport.close();
301 }
302
303 private static void loadBatch(SymmetricEngine engine, String fileName)
304 throws Exception {
305 IDataLoaderService service = (IDataLoaderService) engine
306 .getApplicationContext().getBean(Constants.DATALOADER_SERVICE);
307 File file = new File(fileName);
308 if (file.exists() && file.isFile()) {
309 FileInputStream in = new FileInputStream(file);
310 service.loadData(in, System.out);
311 System.out.flush();
312 in.close();
313
314 } else {
315 throw new FileNotFoundException("Could not find " + fileName);
316 }
317 }
318
319 private static void openRegistration(SymmetricEngine engine, String argument) {
320 argument = argument.replace('\"', ' ');
321 int index = argument.trim().indexOf(",");
322 if (index < 0) {
323 throw new IllegalArgumentException(
324 "Check the argument you passed in. --"
325 + OPTION_OPEN_REGISTRATION
326 + " takes an argument of {groupId},{externalId}");
327 }
328 String nodeGroupId = argument.substring(0, index).trim();
329 String externalId = argument.substring(index + 1).trim();
330 IRegistrationService registrationService = (IRegistrationService) engine
331 .getApplicationContext()
332 .getBean(Constants.REGISTRATION_SERVICE);
333 registrationService.openRegistration(nodeGroupId, externalId);
334 }
335
336 private static String reloadNode(SymmetricEngine engine, String argument) {
337 IDataService dataService = (IDataService) engine
338 .getApplicationContext().getBean(Constants.DATA_SERVICE);
339 return dataService.reloadNode(argument);
340 }
341
342 private static void generateDDL(SymmetricEngine engine, String fileName)
343 throws IOException {
344 File file = new File(fileName);
345 file.getParentFile().mkdirs();
346 FileWriter os = new FileWriter(file, false);
347 os.write(((IDbDialect) engine.getApplicationContext().getBean(
348 Constants.DB_DIALECT)).getCreateSymmetricDDL());
349 os.close();
350 }
351
352 private static void generateDefaultProperties(String fileName)
353 throws IOException {
354 File file = new File(fileName);
355 file.getParentFile().mkdirs();
356 BufferedReader is = new BufferedReader(new InputStreamReader(
357 SymmetricLauncher.class
358 .getResourceAsStream("/symmetric-default.properties"),
359 Charset.defaultCharset()));
360 FileWriter os = new FileWriter(file, false);
361 String line = is.readLine();
362 while (line != null) {
363 os.write(line);
364 os.write(System.getProperty("line.separator"));
365 line = is.readLine();
366 }
367 is.close();
368 os.close();
369 }
370
371 private static void autoCreateDatabase(SymmetricEngine engine) {
372 IBootstrapService bootstrapService = (IBootstrapService) engine
373 .getApplicationContext().getBean(Constants.BOOTSTRAP_SERVICE);
374 bootstrapService.setupDatabase(true);
375 }
376
377 private static void runDdlXml(SymmetricEngine engine, String fileName)
378 throws FileNotFoundException {
379 IDbDialect dialect = (IDbDialect) engine.getApplicationContext()
380 .getBean(Constants.DB_DIALECT);
381 File file = new File(fileName);
382 if (file.exists() && file.isFile()) {
383 Platform pf = dialect.getPlatform();
384 Database db = new DatabaseIO().read(new File(fileName));
385 pf.createTables(db, false, true);
386 } else {
387 throw new FileNotFoundException("Could not find " + fileName);
388 }
389 }
390
391 private static void runSql(SymmetricEngine engine, String fileName)
392 throws FileNotFoundException, MalformedURLException {
393 IDbDialect dialect = (IDbDialect) engine.getApplicationContext()
394 .getBean(Constants.DB_DIALECT);
395 File file = new File(fileName);
396 if (file.exists() && file.isFile()) {
397 SqlScript script = new SqlScript(file.toURL(), dialect
398 .getPlatform().getDataSource());
399 script.execute();
400 } else {
401 throw new FileNotFoundException("Could not find " + fileName);
402 }
403 }
404
405 }