package ru.vyarus.dropwizard.orient.internal; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.io.Files; import com.orientechnologies.orient.core.config.OGlobalConfiguration; import com.orientechnologies.orient.server.OServer; import com.orientechnologies.orient.server.OServerMain; import com.orientechnologies.orient.server.config.OServerConfiguration; import com.orientechnologies.orient.server.config.OServerUserConfiguration; import com.orientechnologies.orient.server.network.OServerNetworkListener; import com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary; import com.orientechnologies.orient.server.network.protocol.http.ONetworkProtocolHttpAbstract; import com.orientechnologies.orient.server.network.protocol.http.command.get.OServerCommandGetStaticContent; import io.dropwizard.lifecycle.Managed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.vyarus.dropwizard.orient.configuration.OrientServerConfiguration; import ru.vyarus.dropwizard.orient.internal.cmd.ApiRedirectCommand; import java.io.File; import java.io.IOException; /** * Orient server managed object. Lifecycle must be managed by dropwizard. * Server will be activated only when 'server' command used (jetty manage lifecycle of Managed objects). * <p>User 'root' must be defined in configuration, otherwise orient will always ask for root user password on start * (it would not be able to store password somewhere). As a side effect default guest user would not be created * (but you can define it in config).</p> * <p>If static handler registered, registers orient studio. * Studio available on url: http://localhost:2480/studio/</p> */ public class EmbeddedOrientServer implements Managed { private final Logger logger = LoggerFactory.getLogger(EmbeddedOrientServer.class); private final OrientServerConfiguration conf; private final ObjectMapper mapper; private final Info serverInfo = new Info(); /** * @param conf orient server configuration object * @param mapper for serializing orient security json from yaml configuration */ public EmbeddedOrientServer(final OrientServerConfiguration conf, final ObjectMapper mapper) { this.conf = validateConfiguration(conf); this.mapper = mapper; } /** * {@inheritDoc} */ @Override public void start() throws Exception { System.setProperty("ORIENTDB_HOME", conf.getFilesPath()); System.setProperty("orientdb.www.path", ""); prepareSecurityConfig(); final OServer server = OServerMain.create(); server.startup(conf.getConfig()).activate(); final OServerNetworkListener httpListener = server.getListenerByProtocol(ONetworkProtocolHttpAbstract.class); boolean studioInstalled = false; if (httpListener != null) { final OServerCommandGetStaticContent command = (OServerCommandGetStaticContent) httpListener .getCommand(OServerCommandGetStaticContent.class); if (command != null) { studioInstalled = new OrientStudioInstaller(command).install(); httpListener.registerStatelessCommand(new ApiRedirectCommand()); } } fillServerInfo(server, studioInstalled); logger.info("Orient server started"); } /** * {@inheritDoc} */ @Override public void stop() throws Exception { OServerMain.server().shutdown(); logger.info("Orient server stopped"); } /** * @return server installation information */ public Info getServerInfo() { return serverInfo; } private OrientServerConfiguration validateConfiguration(final OrientServerConfiguration conf) { Preconditions.checkNotNull(conf, "Configuration object required"); Preconditions.checkNotNull(conf.getConfig(), "Orient server configuration required"); Preconditions.checkState(hasRootUser(conf.getConfig()), "User '%s' must be defined in configuration because otherwise orient will ask " + "for user password on each application start.", OServerConfiguration.DEFAULT_ROOT_USER); return conf; } private void prepareSecurityConfig() { // note: in both cases configured file could be overridden with system property. hope nobody will define it if (conf.getSecurity() != null) { // use the same path as default final String file = conf.getFilesPath() + "/config/security.json"; try { final String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(conf.getSecurity()); final File security = new File(file); Files.createParentDirs(security); Files.write(json, security, Charsets.UTF_8); } catch (IOException e) { throw new IllegalStateException("Failed to write orient security file: " + file, e); } // register path just in case default will change OGlobalConfiguration.SERVER_SECURITY_FILE.setValue(file); conf.getSecurity().textValue(); logger.debug("Orient security configured with file: {}", file); } else if (conf.getSecurityFile() != null) { OGlobalConfiguration.SERVER_SECURITY_FILE.setValue(conf.getSecurityFile()); logger.debug("Orient security file: {}", conf.getSecurityFile()); } } private boolean hasRootUser(final OServerConfiguration conf) { if (conf.users == null) { return false; } boolean res = false; for (OServerUserConfiguration user : conf.users) { if (user.name.equals(OServerConfiguration.DEFAULT_ROOT_USER)) { res = true; break; } } return res; } private void fillServerInfo(final OServer server, final boolean studioInstalled) { serverInfo.studioInstalled = studioInstalled; final OServerNetworkListener httpListener = server.getListenerByProtocol(ONetworkProtocolHttpAbstract.class); if (httpListener != null) { serverInfo.httpPort = String.valueOf(httpListener.getInboundAddr().getPort()); } final OServerNetworkListener binaryListener = server.getListenerByProtocol(ONetworkProtocolBinary.class); if (binaryListener != null) { serverInfo.binaryPort = String.valueOf(binaryListener.getInboundAddr().getPort()); } } /** * Server installation info. */ @SuppressWarnings("checkstyle:VisibilityModifier") public static class Info { public boolean studioInstalled; public String httpPort; public String binaryPort; } }