diff --git a/cadc-vosi/README.md b/cadc-vosi/README.md index 2480044..46c3058 100644 --- a/cadc-vosi/README.md +++ b/cadc-vosi/README.md @@ -8,19 +8,59 @@ cadc-tap-schema library. In addition to the normal VOSI API, the VOSI-availability component supports optional changing of the service state. Supported states: ReadWrite (normal), ReadOnly, and Offline. +Set state example: +``` +curl --cert -d state=ReadOnly https://example.net/srv/availability +``` +Get example: +``` +curl https://example.net/srv/availability +``` + ## cadc-vosi.properties This config file (optional) allows deployers to specify users that are allowed to change the service state via HTTP POST to the `/availability` endpoint. ```properties user = {X509 distinguished name} user = ... + +# optional mode at startup: Offline, ReadOnly, ReadWrite (default) +startupMode = ReadWrite|ReadOnly|Offline ``` + For the X509 distinguished name, the availability endpoint can successfully authorise the user even when the associated AAI system is unavailable, so this mechanism is more robust as it does not depend on any other functioning component. +If an optional _startupMode_ is configured, the availability servlet will create the service-specific +AvailabilityPlugin and call `setState`. The behaviour is completely in the control of the service +and how it implements the plugin. + ## TODO - rewrite the AvailabilityServlet as a RestAction based on the `cadc-rest` library - add ability to configure users and groups consistent with the `cadc-log` library - remove the old CapabilitiesServlet - developer documentation (how to include in web.xml) + +## temporary developer documentation +The following (or something like it) is needed in the `web.xml` deployment descriptor to add +an /avaailability endpoint to a service: +```xml + + + AvailabilityServlet + ca.nrc.cadc.vosi.AvailabilityServlet + + ca.nrc.cadc.vosi.AvailabilityPlugin + fully.qualified.classname.for.AvailabilityPluginImpl + + 2 + + + AvailabilityServlet + /availability + +``` +Especially if using the _startupMode_, putting this servlet early in the load sequence (2 above, before +service specific endpoints and init) will ensure that the startup mode is set before other init happens. + diff --git a/cadc-vosi/build.gradle b/cadc-vosi/build.gradle index 755ce3b..6ab47c1 100644 --- a/cadc-vosi/build.gradle +++ b/cadc-vosi/build.gradle @@ -16,7 +16,7 @@ sourceCompatibility = 1.8 group = 'org.opencadc' -version = '1.4.5' +version = '1.4.6' description = 'OpenCADC VOSI server library' def git_url = 'https://github.com/opencadc/reg' @@ -27,7 +27,7 @@ dependencies { compile 'javax.servlet:javax.servlet-api:[3.1.0,)' compile 'org.opencadc:cadc-util:[1.6,)' - compile 'org.opencadc:cadc-rest:[1.3.6,)' + compile 'org.opencadc:cadc-rest:[1.3.20,)' compile 'org.opencadc:cadc-registry:[1.3.5,)' runtime 'org.jdom:saxpath:1.0-FCS' diff --git a/cadc-vosi/src/main/java/ca/nrc/cadc/vosi/AvailabilityServlet.java b/cadc-vosi/src/main/java/ca/nrc/cadc/vosi/AvailabilityServlet.java index 85e66d0..93a5780 100644 --- a/cadc-vosi/src/main/java/ca/nrc/cadc/vosi/AvailabilityServlet.java +++ b/cadc-vosi/src/main/java/ca/nrc/cadc/vosi/AvailabilityServlet.java @@ -74,10 +74,12 @@ import ca.nrc.cadc.log.ServletLogInfo; import ca.nrc.cadc.log.WebServiceLogInfo; import ca.nrc.cadc.net.NetUtil; +import ca.nrc.cadc.util.InvalidConfigException; import ca.nrc.cadc.util.MultiValuedProperties; import ca.nrc.cadc.util.PropertiesReader; import ca.nrc.cadc.util.StringUtil; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.security.Principal; import java.util.HashSet; import java.util.List; @@ -103,6 +105,7 @@ public class AvailabilityServlet extends HttpServlet { private static final long serialVersionUID = 201003131300L; private static final String AVAILABILITY_PROPERTIES = "cadc-vosi.properties"; + private static final String MODE_KEY = "startupMode"; private static final String USERS_PROPERTY = "user"; private String pluginClassName; @@ -115,6 +118,27 @@ public void init(ServletConfig config) this.appName = config.getServletContext().getContextPath().substring(1).replaceAll("/", "-"); this.pluginClassName = config.getInitParameter(AvailabilityPlugin.class.getName()); log.info("application: " + appName + " plugin impl: " + pluginClassName); + + MultiValuedProperties mvp = getAvailabilityProperties(); + String startupMode = mvp.getFirstPropertyValue(MODE_KEY); + if (startupMode != null) { + AvailabilityPlugin ap = loadPlugin(); + ap.setState(startupMode); + } + } + + private AvailabilityPlugin loadPlugin() throws InvalidConfigException { + try { + Class wsClass = Class.forName(pluginClassName); + AvailabilityPlugin ap = (AvailabilityPlugin) wsClass.getConstructor().newInstance(); + ap.setAppName(appName); + log.debug("loaded: " + wsClass); + return ap; + } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException + | InstantiationException | NoSuchMethodException | SecurityException + | InvocationTargetException ex) { + throw new InvalidConfigException("failed to load AvailabilityPlugin: " + pluginClassName, ex); + } } @Override @@ -128,9 +152,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) logInfo.setSubject(subject); log.info(logInfo.start()); - Class wsClass = Class.forName(pluginClassName); - AvailabilityPlugin ap = (AvailabilityPlugin) wsClass.newInstance(); - ap.setAppName(appName); + AvailabilityPlugin ap = loadPlugin(); String detail = request.getParameter("detail"); if (detail != null && detail.equals("min")) { @@ -174,9 +196,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) logInfo.setSubject(subject); log.info(logInfo.start()); - Class wsClass = Class.forName(pluginClassName); - AvailabilityPlugin ap = (AvailabilityPlugin) wsClass.newInstance(); - ap.setAppName(appName); + AvailabilityPlugin ap = loadPlugin(); IdentityManager im = AuthenticationUtil.getIdentityManager(); String caller = im.toDisplayString(subject); diff --git a/cadc-vosi/src/main/java/ca/nrc/cadc/vosi/CapInitAction.java b/cadc-vosi/src/main/java/ca/nrc/cadc/vosi/CapInitAction.java index 2c166d3..766b21b 100644 --- a/cadc-vosi/src/main/java/ca/nrc/cadc/vosi/CapInitAction.java +++ b/cadc-vosi/src/main/java/ca/nrc/cadc/vosi/CapInitAction.java @@ -70,6 +70,7 @@ import ca.nrc.cadc.reg.Capabilities; import ca.nrc.cadc.reg.CapabilitiesReader; import ca.nrc.cadc.rest.InitAction; +import ca.nrc.cadc.rest.Version; import ca.nrc.cadc.util.StringUtil; import java.io.StringReader; @@ -189,7 +190,7 @@ public void doInit() { } try { - String version = findLibraryVersion(CapInitAction.class); + Version version = getLibraryVersion(CapInitAction.class); jndiKey = componentID + ".version"; try { @@ -198,46 +199,10 @@ public void doInit() { } catch (NamingException e) { log.debug("no previously bound value, continuting"); } - initContext.bind(jndiKey, version); + initContext.bind(jndiKey, version.getMajorMinor()); log.info("doInit: version=" + version + " stored via JNDI: " + jndiKey); } catch (Exception ex) { throw new IllegalArgumentException("CONFIG: failed to set version flag", ex); } } - - // TODO: move this code up to cadc-rest or cadc-util - private String findLibraryVersion(Class probe) { - String ret = "no-version-found"; - String rname = probe.getSimpleName() + ".class"; - try { - - URL resURL = probe.getResource(rname); - log.debug("library URL: " + resURL); - // assume assume maven-central naming conventions for jar - // jar:file:/path/to/{library}-{ver}.jar!/package/subpackage/{rname} - if (resURL != null) { - String[] parts = resURL.toExternalForm().split("[:!]"); - int i = 0; - for (String p : parts) { - if (p.endsWith(".jar")) { - int s = p.lastIndexOf('/'); - String ver = p.substring(s + 1); // {library}-{ver}.jar - ver = ver.replace(".jar", ""); // {library}-{ver} - - // extract {major}.{minor} only - String[] mmp = ver.split("\\."); - if (mmp.length > 2) { - ret = mmp[0] + "." + mmp[1]; - } else { - ret = ver; - } - } - } - } - } catch (Exception ex) { - log.error("failed to find version for " + rname + " from classpath", ex); - } - - return ret; - } }