-
Notifications
You must be signed in to change notification settings - Fork 42
/
Copy pathApollo.java
361 lines (307 loc) · 16.1 KB
/
Apollo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/*
* Copyright © 2018-2019 Apollo Foundation
*/
package com.apollocurrency.aplwallet.apl.exec;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import com.apollocurrency.aplwallet.apl.conf.ConfPlaceholder;
import com.apollocurrency.aplwallet.apl.core.app.AplCoreRuntime;
import com.apollocurrency.aplwallet.apl.core.chainid.BlockchainConfig;
import com.apollocurrency.aplwallet.apl.core.chainid.BlockchainConfigUpdater;
import com.apollocurrency.aplwallet.apl.core.service.appdata.SecureStorageService;
import com.apollocurrency.aplwallet.apl.core.utils.LegacyDbUtil;
import com.apollocurrency.aplwallet.apl.udpater.intfce.UpdaterCore;
import com.apollocurrency.aplwallet.apl.updater.core.UpdaterCoreImpl;
import com.apollocurrency.aplwallet.apl.util.Constants;
import com.apollocurrency.aplwallet.apl.util.StringUtils;
import com.apollocurrency.aplwallet.apl.util.cdi.AplContainer;
import com.apollocurrency.aplwallet.apl.util.cdi.AplContainerBuilder;
import com.apollocurrency.aplwallet.apl.util.db.MariaDbProcess;
import com.apollocurrency.aplwallet.apl.util.env.EnvironmentVariables;
import com.apollocurrency.aplwallet.apl.util.env.PosixExitCodes;
import com.apollocurrency.aplwallet.apl.util.env.RuntimeEnvironment;
import com.apollocurrency.aplwallet.apl.util.env.RuntimeMode;
import com.apollocurrency.aplwallet.apl.util.env.RuntimeParams;
import com.apollocurrency.aplwallet.apl.util.env.config.Chain;
import com.apollocurrency.aplwallet.apl.util.env.config.ChainUtils;
import com.apollocurrency.aplwallet.apl.util.env.config.ChainsConfigLoader;
import com.apollocurrency.aplwallet.apl.util.env.config.PropertiesConfigLoader;
import com.apollocurrency.aplwallet.apl.util.env.dirprovider.ConfigDirProvider;
import com.apollocurrency.aplwallet.apl.util.env.dirprovider.ConfigDirProviderFactory;
import com.apollocurrency.aplwallet.apl.util.env.dirprovider.DirProvider;
import com.apollocurrency.aplwallet.apl.util.env.dirprovider.DirProviderFactory;
import com.apollocurrency.aplwallet.apl.util.env.dirprovider.PredefinedDirLocations;
import com.apollocurrency.aplwallet.apl.util.injectable.PropertiesHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.enterprise.inject.spi.CDI;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import picocli.CommandLine;
/**
* Main Apollo startup class
*
* @author [email protected]
*/
public class Apollo {
// System properties to load by PropertiesConfigLoader
public static final String PID_FILE = "apl.pid";
public static final String CMD_FILE = "apl.cmdline";
public static final String APP_FILE = "apl.app";
private static final List<String> SYSTEM_PROPERTY_NAMES = Arrays.asList(
"socksProxyHost",
"socksProxyPort",
"apl.enablePeerUPnP",
"apl.enableAPIUPnP"
);
private final static String[] VALID_LOG_LEVELS = {"ERROR", "WARN", "INFO", "DEBUG", "TRACE"};
//This variable is used in LogDirPropertyDefiner configured in logback.xml
public static Path logDirPath = Paths.get("");
public static RuntimeMode runtimeMode;
public static DirProvider dirProvider;
//initially we do not have control over MariaDB process, it could be startted externally or system-wide
public static MariaDbProcess mariaDbProcess = null;
//We have dir provider configured in logback.xml so should init log later
private static Logger log;
private static AplContainer container;
private static AplCoreRuntime aplCoreRuntime;
private static void setLogLevel(int logLevel) {
// let's SET LEVEL EXPLOCITLY only when it was passed via command line params
String packageName = "com.apollocurrency.aplwallet.apl";
if (logLevel >= VALID_LOG_LEVELS.length - 1) {
logLevel = VALID_LOG_LEVELS.length - 1;
}
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger logger = loggerContext.getLogger(packageName);
System.out.println(packageName + " current logger level: " + logger.getLevel()
+ " New level: " + VALID_LOG_LEVELS[logLevel]);
logger.setLevel(Level.toLevel(VALID_LOG_LEVELS[logLevel]));
// otherwise we want to load usual logback.xml settings
}
public static boolean saveStartParams(String[] argv, String pidPath, ConfigDirProvider configDirProvider) {
boolean res = true;
String cmdline = "";
for (String s : argv) {
cmdline = cmdline + s + " ";
}
Path hp = Paths.get(configDirProvider.getUserConfigLocation());
String home = hp.toString() + File.separator;
File dir = new File(home);
if (!dir.exists()) {
dir.mkdirs();
}
String path = pidPath.isEmpty() ? home + PID_FILE : pidPath;
try (PrintWriter out = new PrintWriter(path)) {
out.println(RuntimeParams.getProcessId());
} catch (FileNotFoundException ex) {
System.err.println("Can not write PID to: " + path);
res = false;
}
path = home + CMD_FILE;
try (PrintWriter out = new PrintWriter(path)) {
out.println(cmdline);
} catch (FileNotFoundException ex) {
System.err.println("Can not write command line args file to: " + path);
res = false;
}
path = home + APP_FILE;
try (PrintWriter out = new PrintWriter(home + APP_FILE)) {
out.println(DirProvider.getBinDir());
} catch (FileNotFoundException ex) {
System.err.println("Can not write Apollo start path file to: " + path);
res = false;
}
return res;
}
public static PredefinedDirLocations merge(CmdLineArgs args, EnvironmentVariables vars, CustomDirLocations customDirLocations) {
return new PredefinedDirLocations(
customDirLocations.getDbDir().isEmpty() ? StringUtils.isBlank(args.dbDir) ? vars.dbDir : args.dbDir : customDirLocations.getDbDir().get(),
StringUtils.isBlank(args.logDir) ? vars.logDir : args.logDir,
customDirLocations.getKeystoreDir().isEmpty() ? StringUtils.isBlank(args.vaultKeystoreDir) ? vars.vaultKeystoreDir : args.vaultKeystoreDir : customDirLocations.getKeystoreDir().get(),
StringUtils.isBlank(args.pidFile) ? vars.pidFile : args.pidFile,
StringUtils.isBlank(args.twoFactorAuthDir) ? vars.twoFactorAuthDir : args.twoFactorAuthDir,
StringUtils.isBlank(args.dataExportDir) ? vars.dataExportDir : args.dataExportDir,
StringUtils.isBlank(args.dexKeystoreDir) ? vars.dexKeystoreDir : args.dexKeystoreDir
);
}
public static void setSystemProperties(CmdLineArgs args){
System.setProperty("apl.runtime.mode", args.serviceMode ? "service" : "user");
System.setProperty("javax.net.ssl.trustStore", "cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
System.setProperty("javax.net.ssl.trustStoreType", "JKS");
}
private static String getCustomDbPath(UUID chainId, Properties properties) { //maybe better to set dbUrl or add to dirProvider
String customDbDir = properties.getProperty(CustomDirLocations.DB_DIR_PROPERTY_NAME);
if (customDbDir != null) {
Path legacyHomeDir = LegacyDbUtil.getLegacyHomeDir();
Path customDbPath = legacyHomeDir.resolve(customDbDir).resolve(chainId.toString().substring(0, 6)).normalize();
System.out.println("Using custom db path " + customDbPath.toAbsolutePath().toString());
return customDbPath.toAbsolutePath().toString();
}
return null;
}
private void initUpdater(String attachmentFilePath, boolean debug, PropertiesHolder propertiesHolder) {
if (!propertiesHolder.getBooleanProperty("apl.allowUpdates", false)) {
return;
}
UpdaterCore updaterCore = CDI.current().select(UpdaterCoreImpl.class).get();
updaterCore.init(attachmentFilePath, debug);
}
/**
* @param argv the command line arguments
*/
public static void main(String[] argv) {
System.out.println("Initializing Apollo");
Apollo app = new Apollo();
//parse command line first
CmdLineArgs args = new CmdLineArgs();
CommandLine pc = new CommandLine(args);
try {
CommandLine.ParseResult parseResult = pc.parseArgs(argv);
if (args.help) {
pc.usage(System.err);
System.exit(PosixExitCodes.OK.exitCode());
}
} catch (RuntimeException ex) {
System.err.println("Error parsing command line arguments.");
System.err.println(ex.getMessage());
pc.usage(System.err);
System.exit(PosixExitCodes.EX_USAGE.exitCode());
}
if (args.getNetIdx() >= 0 && !args.chainId.isEmpty()) {
System.err.println("--chainId, --testnet and --net parameters are incompatible, please specify only one");
System.exit(PosixExitCodes.EX_USAGE.exitCode());
}
//set main application class to runtime
RuntimeEnvironment.getInstance().setMain(Apollo.class);
//set some important system properties
setSystemProperties(args);
//cheat classloader to get access to "conf" package resources
ConfPlaceholder ph = new ConfPlaceholder();
//--------------- config locading section -------------------------------------
//load configuration files
EnvironmentVariables envVars = new EnvironmentVariables(Constants.APPLICATION_DIR_NAME);
String configDir = StringUtils.isBlank(args.configDir) ? envVars.configDir : args.configDir;
ConfigDirProviderFactory.setup(args.serviceMode, Constants.APPLICATION_DIR_NAME, args.netIdx, args.chainId, configDir);
ConfigDirProvider configDirProvider = ConfigDirProviderFactory.getConfigDirProvider();
//save command line params and PID
if (!saveStartParams(argv, args.pidFile, configDirProvider)) {
System.exit(PosixExitCodes.EX_CANTCREAT.exitCode());
}
// Well, we can not resolve chainID we have to run with from given parameters
// and therefore can not read configs. We have to exit program
if (configDirProvider.getChainId() == null) {
System.err.println("ERROR: Can not resolve chain ID to run with from given command line arguments of configs!");
System.exit(PosixExitCodes.EX_CONFIG.exitCode());
}
//load configuration files
PropertiesConfigLoader propertiesLoader = new PropertiesConfigLoader(
configDirProvider,
args.isResourceIgnored(),
configDir,
Constants.APPLICATION_DIR_NAME + ".properties",
SYSTEM_PROPERTY_NAMES);
// load everuthing into applicationProperies. This is the place where all configuration
// is collected from configs, command line and environment variables
Properties applicationProperties = propertiesLoader.load();
ChainsConfigLoader chainsConfigLoader = new ChainsConfigLoader(
configDirProvider,
configDir,
args.isResourceIgnored()
);
// init chains configurations by loading chains.json file
Map<UUID, Chain> chains = chainsConfigLoader.load();
UUID chainId = ChainUtils.getActiveChain(chains).getChainId();
//over-write config options from command line if set
if (args.noShardImport) {
applicationProperties.setProperty("apl.noshardimport", "" + args.noShardImport);
}
if (args.noShardCreate) {
applicationProperties.setProperty("apl.noshardcreate", "" + args.noShardCreate);
}
//TODO: check this piece of art
CustomDirLocations customDirLocations = new CustomDirLocations(
getCustomDbPath(chainId, applicationProperties),
applicationProperties.getProperty(CustomDirLocations.KEYSTORE_DIR_PROPERTY_NAME)
);
DirProviderFactory.setup(args.serviceMode,
chainId,
Constants.APPLICATION_DIR_NAME,
merge(args, envVars, customDirLocations)
);
dirProvider = DirProviderFactory.getProvider();
RuntimeEnvironment.getInstance().setDirProvider(dirProvider);
//init logging
logDirPath = dirProvider.getLogsDir().toAbsolutePath();
log = LoggerFactory.getLogger(Apollo.class);
if (args.debug != CmdLineArgs.DEFAULT_DEBUG_LEVEL) {
setLogLevel(args.debug);
}
System.out.println("=== INFO: Bin directory of apollo-blockchain is: " + DirProvider.getBinDir().toAbsolutePath()+" ===");
// runtimeMode could be user or service. It is also different for Unix and Windows
runtimeMode = RuntimeEnvironment.getInstance().getRuntimeMode();
runtimeMode.init(); // instance is NOT PROXIED by CDI !!
//-------------- now bring CDI container up! -------------------------------------
//Configure CDI Container builder and start CDI container. From now all things must go CDI way
AplContainerBuilder aplContainerBuilder = AplContainer.builder().containerId("MAIN-APL-CDI")
// do not use recursive scan because it violates the restriction to
// deploy one bean for all deployment archives
// Recursive scan will trigger base synthetic archive to load JdbiTransactionalInterceptor, which was already loaded by apl-core archive
// See https://docs.jboss.org/cdi/spec/2.0.EDR2/cdi-spec.html#se_bootstrap for more details
// we already have it in beans.xml in core
.annotatedDiscoveryMode();
//!!!!!!!!!!!!!!
//TODO: turn it on periodically in development process to check CDI errors
// Enable for development only, see http://weld.cdi-spec.org/news/2015/11/10/weld-probe-jmx/
// run with ./bin/apl-run-jmx.sh
//
// aplContainerBuilder.devMode();
//
//!!!!!!!!!!!!!!!
if (args.disableWeldConcurrentDeployment) {
//It's very helpful when the application is stuck during the Weld Container building.
log.info("The concurrent deployment of Weld container is disabled.");
aplContainerBuilder.disableConcurrentDeployment();
}
//init CDI container
container = aplContainerBuilder.build();
log.debug("Weld CDI container build done");
// ------------------- NOW CDI is up and running, we have feed our configs to beans
//aplCoreRuntime is the producer for all config holders, initing it with configs
aplCoreRuntime = CDI.current().select(AplCoreRuntime.class).get();
aplCoreRuntime.init(runtimeMode, dirProvider, applicationProperties, chains);
BlockchainConfigUpdater blockchainConfigUpdater = CDI.current().select(BlockchainConfigUpdater.class).get();
blockchainConfigUpdater.updateChain(aplCoreRuntime.getChainsConfigHolder().getActiveChain(), aplCoreRuntime.getPropertieHolder());
// init secureStorageService instance via CDI for 'ShutdownHook' constructor below
SecureStorageService secureStorageService = CDI.current().select(SecureStorageService.class).get();
aplCoreRuntime = CDI.current().select(AplCoreRuntime.class).get();
BlockchainConfig blockchainConfig = CDI.current().select(BlockchainConfig.class).get();
if (log != null) {
log.trace("{}",aplCoreRuntime.getPropertieHolder().dumpAllProperties()); // dumping all properties
}
try {
// updated shutdown hook explicitly created with instances
Runtime.getRuntime().addShutdownHook(new ShutdownHook(aplCoreRuntime));
aplCoreRuntime.addCoreAndInit();
app.initUpdater(args.updateAttachmentFile, args.debugUpdater, aplCoreRuntime.getPropertieHolder());
} catch (Throwable t) {
System.out.println("Fatal error: " + t.toString());
t.printStackTrace();
}
}
public static void shutdownWeldContainer() {
try {
container.shutdown();
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
}
}