1 package emissary.command;
2
3 import emissary.client.EmissaryResponse;
4 import emissary.command.converter.ModeConverter;
5 import emissary.command.converter.ProjectBaseConverter;
6 import emissary.directory.EmissaryNode;
7 import emissary.server.EmissaryServer;
8 import emissary.server.api.Pause;
9
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12 import picocli.CommandLine.Command;
13 import picocli.CommandLine.Option;
14
15 import java.nio.file.Path;
16 import java.util.LinkedHashSet;
17 import java.util.Locale;
18 import java.util.Set;
19 import java.util.concurrent.TimeUnit;
20
21 import static emissary.directory.EmissaryNode.STRICT_STARTUP_MODE;
22
23 @Command(description = "Start an Emissary jetty server", subcommands = {HelpCommand.class})
24 public class ServerCommand extends ServiceCommand {
25 private static final Logger LOG = LoggerFactory.getLogger(ServerCommand.class);
26
27 public static final String COMMAND_NAME = "server";
28
29 public static final int DEFAULT_PORT = 8001;
30
31 @Option(names = {"-m", "--mode"}, description = "mode: standalone or cluster\nDefault: ${DEFAULT-VALUE}", converter = ModeConverter.class,
32 defaultValue = "standalone")
33 private EmissaryNode.Mode mode;
34
35 @Option(names = "--staticDir", description = "path to static assets, loaded from classpath otherwise", converter = ProjectBaseConverter.class)
36 private Path staticDir;
37
38 @Option(names = {"-a", "--agents"}, description = "number of mobile agents (default is based on memory)\nDefault: ${DEFAULT-VALUE}")
39 private int agents;
40
41 @Option(names = {"-t", "--timeout"}, description = "max amount of time to attempt a server refresh (in minutes) \nDefault: ${DEFAULT-VALUE}")
42 private int timeout;
43
44 @Option(names = {"--dumpJettyBeans"}, description = "dump all the jetty beans that loaded\nDefault: ${DEFAULT-VALUE}")
45 private boolean dumpJettyBeans = false;
46
47 @Option(names = {"--strict"}, description = "If one Place fails to start, shut down the entire server\nDefault: ${DEFAULT-VALUE}")
48 private boolean strictMode = false;
49
50 @Override
51 public String getCommandName() {
52 return COMMAND_NAME;
53 }
54
55 @Override
56 public int getDefaultPort() {
57 return DEFAULT_PORT;
58 }
59
60 public EmissaryNode.Mode getMode() {
61 return mode;
62 }
63
64 public Path getStaticDir() {
65 return staticDir;
66 }
67
68 public int getAgents() {
69 return agents;
70 }
71
72 public long getTimeout() {
73 return timeout;
74 }
75
76 public boolean shouldDumpJettyBeans() {
77 return dumpJettyBeans;
78 }
79
80
81
82
83
84
85 public boolean shouldStrictMode() {
86 return strictMode;
87 }
88
89 @Override
90 public void setupCommand() {
91 setupHttp();
92 if (getTimeout() > 0) {
93 System.setProperty(EmissaryNode.NODE_REFRESH_TIMEOUT_PROPERTY, String.valueOf(TimeUnit.MINUTES.toMillis(getTimeout())));
94 }
95
96 reinitLogback();
97 setupServer();
98 }
99
100 public void setupServer() {
101 String flavorMode;
102 if (getFlavor() == null) {
103 flavorMode = getMode().toString();
104 } else {
105 flavorMode = getMode().toString() + "," + getFlavor();
106 }
107
108 if (shouldStrictMode()) {
109 System.setProperty(STRICT_STARTUP_MODE, "true");
110 }
111
112
113 Set<String> flavorSet = new LinkedHashSet<>();
114 for (String f : flavorMode.split(",")) {
115 flavorSet.add(f.toUpperCase(Locale.getDefault()));
116 }
117
118 if (flavorSet.contains("STANDALONE") && flavorSet.contains("CLUSTER")) {
119 throw new IllegalArgumentException("Can not run a server in both STANDALONE and CLUSTER");
120 } else {
121 overrideFlavor(String.join(",", flavorSet));
122 }
123 }
124
125 @Override
126 protected void startService() {
127 LOG.info("Running Emissary Server");
128 EmissaryServer.init(this).startServer();
129 }
130
131 @Override
132 protected void refreshService() {
133 EmissaryResponse response = performPost(getServiceRefreshEndpoint());
134 if (response.getStatus() != 200) {
135 LOG.error("Failed to {} Emissary services: {}", isInvalidate() ? "invalidate" : "refresh", response.getContentString());
136 } else {
137 LOG.info("{} Emissary services", isInvalidate() ? "Invalidating" : "Refreshing");
138 }
139 }
140
141 @Override
142 protected void pauseService() {
143 setServerState(Pause.PAUSE);
144 }
145
146 @Override
147 protected void unpauseService() {
148 setServerState(Pause.UNPAUSE);
149 }
150
151 protected void setServerState(String state) {
152 LOG.debug("Setting state to {} for EmissaryServer", state);
153 EmissaryResponse response = performPost("/api/" + state);
154 if (response.getStatus() != 200) {
155 LOG.error("Setting Emissary server state to {} failed -- {}", state, response.getContentString());
156 } else {
157 LOG.info("Setting Emissary server state to {} successful", state);
158 }
159 }
160 }