package ca.uhn.fhir.cli;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import ca.uhn.fhir.jpa.demo.ContextHolder;
import ca.uhn.fhir.jpa.demo.FhirServerConfig;
import ca.uhn.fhir.jpa.demo.FhirServerConfigDstu3;
public class RunServerCommand extends BaseCommand {
private static final String OPTION_LOWMEM = "lowmem";
private static final String OPTION_ALLOW_EXTERNAL_REFS = "allow-external-refs";
private static final int DEFAULT_PORT = 8080;
private static final String OPTION_P = "p";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RunServerCommand.class);
private int myPort;
private Server myServer;
@Override
public String getCommandName() {
return "run-server";
}
@Override
public Options getOptions() {
Options options = new Options();
addFhirVersionOption(options);
options.addOption(OPTION_P, "port", true, "The port to listen on (default is " + DEFAULT_PORT + ")");
options.addOption(null, OPTION_LOWMEM, false, "If this flag is set, the server will operate in low memory mode (some features disabled)");
options.addOption(null, OPTION_ALLOW_EXTERNAL_REFS, false, "If this flag is set, the server will allow resources to be persisted contaning external resource references");
return options;
}
private int parseOptionInteger(CommandLine theCommandLine, String opt, int defaultPort) throws ParseException {
try {
return Integer.parseInt(theCommandLine.getOptionValue(opt, Integer.toString(defaultPort)));
} catch (NumberFormatException e) {
throw new ParseException("Invalid value '" + theCommandLine.getOptionValue(opt) + " (must be numeric)");
}
}
@Override
public void run(CommandLine theCommandLine) throws ParseException {
myPort = parseOptionInteger(theCommandLine, OPTION_P, DEFAULT_PORT);
if (theCommandLine.hasOption(OPTION_LOWMEM)) {
ourLog.info("Running in low memory mode, some features disabled");
System.setProperty(OPTION_LOWMEM, OPTION_LOWMEM);
}
if (theCommandLine.hasOption(OPTION_ALLOW_EXTERNAL_REFS)) {
ourLog.info("Server is configured to allow external references");
ContextHolder.setAllowExternalRefs(true);
}
ContextHolder.setCtx(getSpecVersionContext(theCommandLine));
ourLog.info("Preparing HAPI FHIR JPA server on port {}", myPort);
File tempWarFile;
try {
tempWarFile = File.createTempFile("hapi-fhir", ".war");
tempWarFile.deleteOnExit();
InputStream inStream = RunServerCommand.class.getResourceAsStream("/hapi-fhir-cli-jpaserver.war");
OutputStream outStream = new BufferedOutputStream(new FileOutputStream(tempWarFile, false));
IOUtils.copy(inStream, outStream);
} catch (IOException e) {
ourLog.error("Failed to create temporary file", e);
return;
}
final ContextLoaderListener cll = new ContextLoaderListener();
ourLog.info("Starting HAPI FHIR JPA server in {} mode", ContextHolder.getCtx().getVersion().getVersion());
WebAppContext root = new WebAppContext();
root.setAllowDuplicateFragmentNames(true);
root.setWar(tempWarFile.getAbsolutePath());
root.setParentLoaderPriority(true);
root.setContextPath("/");
root.addEventListener(new ServletContextListener() {
@Override
public void contextInitialized(ServletContextEvent theSce) {
theSce.getServletContext().setInitParameter(ContextLoader.CONTEXT_CLASS_PARAM, AnnotationConfigWebApplicationContext.class.getName());
switch (ContextHolder.getCtx().getVersion().getVersion()) {
case DSTU2:
theSce.getServletContext().setInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, FhirServerConfig.class.getName());
break;
case DSTU3:
theSce.getServletContext().setInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, FhirServerConfigDstu3.class.getName());
break;
case DSTU1:
case DSTU2_1:
case DSTU2_HL7ORG:
break;
}
cll.contextInitialized(theSce);
}
@Override
public void contextDestroyed(ServletContextEvent theSce) {
cll.contextDestroyed(theSce);
}
});
String path = ContextHolder.getPath();
root.addServlet("ca.uhn.fhir.jpa.demo.JpaServerDemo", path + "*");
myServer = new Server(myPort);
myServer.setHandler(root);
try {
myServer.start();
} catch (SocketException e) {
throw new CommandFailureException("Server failed to start on port " + myPort + " because of the following error \"" + e.toString() + "\". Note that you can use the '-p' option to specify an alternate port.");
} catch (Exception e) {
ourLog.error("Server failed to start", e);
throw new CommandFailureException("Server failed to start", e);
}
ourLog.info("Server started on port {}", myPort);
ourLog.info("Web Testing UI : http://localhost:{}/", myPort);
ourLog.info("Server Base URL: http://localhost:{}{}", myPort, path);
}
public static void main(String[] theArgs) {
Server server = new Server(22);
String path = "../hapi-fhir-cli-jpaserver";
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/");
webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml");
webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-example");
webAppContext.setParentLoaderPriority(true);
server.setHandler(webAppContext);
try {
server.start();
} catch (Exception e) {
e.printStackTrace();
}
ourLog.info("Started");
}
@Override
public String getCommandDescription() {
return "Start a FHIR server which can be used for testing";
}
}