Commits

Rick Herrick committed 295c98f

XNAT-2610: Enhanced ability to handle anonymization scripts. Added check for disabled site-wide anon script, now pulls whole config instead of just contents. Merged with the series import filter JSON handling.

Comments (0)

Files changed (6)

src/main/java/org/nrg/net/JSONResultExtractor.java

 	 * (non-Javadoc)
 	 * @see org.nrg.net.HttpURLConnectionProcessor#process(java.net.HttpURLConnection)
 	 */
-	public void process(final HttpURLConnection connection)
-	throws IOException,JSONException {
+	public void process(final HttpURLConnection connection) throws IOException, JSONException {
 		final InputStream in = connection.getInputStream();
 		try {
 			final JSONArray entries = RestServer.extractResultFromEntity(RestServer.extractJSONEntity(in));
 			for (int i = 0; i < entries.length(); i++) {
 				decoder.decode(entries.getJSONObject(i));
 			}
-			return;
 		} finally {
 			in.close();
 		}

src/main/java/org/nrg/net/RestServer.java

 import org.json.JSONTokener;
 import org.netbeans.spi.wizard.ResultProgressHandle;
 import org.nrg.IOUtils;
-import org.nrg.net.xnat.SeriesImportFilterApplicatorRetriever;
 import org.nrg.util.Base64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
     private final Logger logger = LoggerFactory.getLogger(RestServer.class);
     private final URL _base;
     private final JSESSIONIDCookie _jsessionidCookie;
+    private String _siteWideAnonScript = null;
 
     public RestServer(final URL url, final JSESSIONIDCookie jsessionidCookie) {
 
     private final Map<Service, PasswordAuthentication> passStore = Maps.newHashMap();
     private final Map<Service, String> descriptions = Maps.newHashMap();
 
-    public Map<String, String> getSeriesImportFilter(final String path) throws IOException, JSONException {
+    public Map<String, Object> getSeriesImportFilter(final String path) throws IOException, JSONException {
         final String query;
         if (!path.contains("format=json")) {
             try {
         } else {
             query = "";
         }
-        final JSONSeriesImportFilterExtractor extractor = new JSONSeriesImportFilterExtractor(path + query);
-        doGet(path, extractor);
-        return extractor.getKeys();
+
+        final JSONConfigurationExtractor extractor = new JSONConfigurationExtractor();
+        extractor.put("mode", "blacklist");
+        extractor.put("list", "");
+        doGet(path + query, extractor);
+        return extractor;
+    }
+
+    public boolean isSiteWideAnonScriptEnabled() throws IOException, JSONException {
+        final JSONConfigurationExtractor extractor = new JSONConfigurationExtractor();
+        extractor.put("status", "disabled");
+        extractor.put("contents", "");
+        doGet("/data/config/anon/script?format=json", extractor);
+        boolean enabled = extractor.get("status").equals("enabled");
+        if (enabled) {
+            _siteWideAnonScript = (String) extractor.get("contents");
+        }
+        return enabled;
+    }
+
+    public String getSiteWideAnonScript() {
+        if (_siteWideAnonScript == null) {
+            return "";
+        }
+        return _siteWideAnonScript;
+    }
+
+    public InputStream getSiteWideAnonScriptAsStream() {
+        return new ByteArrayInputStream(getSiteWideAnonScript().getBytes());
     }
 
     private static final class Service {
         return null == s || "".equals(s);
     }
 
-    private static final class JSONSeriesImportFilterExtractor implements JSONDecoder {
-        final String _path;
-        final private Map<String, String> _keys = new HashMap<String, String>();
-
-        JSONSeriesImportFilterExtractor(final String path) {
-            _path = path;
-        }
+    /**
+     * Turns a JSON object from the XNAT configuration service into a map. Before calling the {@link #decode(org.json.JSONObject)}
+     * method, you can call the {@link #put(Object, Object)} method to set default values for one or more fields in the
+     * configuration.
+     */
+    private static final class JSONConfigurationExtractor extends HashMap<String, Object> implements JSONDecoder {
 
         @Override
-        public void decode(final JSONObject o) throws JSONException {
-            final String status = o.has("status") ? o.getString("status") : null;
-            if (StringUtils.isBlank(status) || !status.equals("enabled")) {
-                _keys.put("status", "disabled");
-                return;
-            } else {
-                _keys.put("status", "enabled");
+        public void decode(final JSONObject json) throws JSONException {
+            final Iterator keys = json.keys();
+            while (keys.hasNext()) {
+                final String key = (String) keys.next();
+                put(key, json.get(key));
             }
-            final String contents = o.has("contents") ? o.getString("contents") : null;
-            if (StringUtils.isBlank(contents)) {
-                _keys.put("mode", "blacklist");
-                _keys.put("list", "");
+            if (containsKey("status") && !get("status").equals("enabled")) {
+                put("status", "disabled");
                 return;
             }
-            JSONObject filters = new JSONObject(new JSONTokener(contents));
-            _keys.put("mode", filters.has("mode")? filters.getString("mode") : "");
-            _keys.put("list", filters.has("list") ? filters.getString("list") : "");
-        }
-
-        public Map<String, String> getKeys() {
-            return _keys;
+            if (containsKey("contents")) {
+                final String contents = (String) get("contents");
+                try {
+                    JSONObject translated = new JSONObject(new JSONTokener(contents));
+                    Map<String, Object> decoded = new HashMap<String, Object>();
+                    final Iterator contentKeys = translated.keys();
+                    while(contentKeys.hasNext()) {
+                        final String key = (String) contentKeys.next();
+                        decoded.put(key, translated.has(key) ? translated.get(key) : "");
+                    }
+                    put("contents", decoded);
+                } catch (JSONException ignored) {
+                    // If we get a JSONException, then the contents isn't something we know how to work with.
+                    // Leave the contents alone.
+                }
+            }
         }
     }
 
     }
 
     private static String makeBasicAuthorization(final PasswordAuthentication auth) {
-        final StringBuilder unencoded = new StringBuilder();
-        unencoded.append(auth.getUserName());
-        unencoded.append(":");
-        unencoded.append(auth.getPassword());
-
-        final StringBuilder enc = new StringBuilder("Basic ");
-        enc.append(Base64.encode(unencoded.toString()));
-        return enc.toString();
+        return "Basic " + Base64.encode(auth.getUserName() + ":" + Arrays.toString(auth.getPassword()));
     }
 
     private static void addBasicAuthorizationToHeaderMap(final Map<String, String> m,
     private static String getErrorEntity(final HttpURLConnection connection) throws IOException {
         final InputStream errorStream = connection.getErrorStream();
         try {
-            if (null == errorStream) {
+            if (null != errorStream) {
                 final ByteArrayOutputStream stream = new ByteArrayOutputStream();
                 IOUtils.copy(stream, errorStream);
                 if (stream.size() > 0) {
     /**
      * Adds an authorization-equivalent header to the provided request: a JSESSION cookie if
      * that's available, or Authorization: Basic otherwise.
-     * @param connection
-     * @return connection
+     * @param connection The connection to which basic authentication should be added.
+     * @return connection The initialized connection.
      */
     public URLConnection addAuthorization(final URLConnection connection) {
         if ("".equals(_jsessionidCookie.toString())) {

src/main/java/org/nrg/net/xnat/BaseScriptApplicatorRetreiver.java

 
         public ApplicatorT getApplicator() { return applicator; }
     }
-
-
-    /**
-     * Creates a ScriptApplicator from the script in the named resource.
-     * @param resourcePath location of the script to be parsed
-     * @return applicator built from the named script
-     * @throws Exception
-     */
-    public static final <ApplicatorT> ApplicatorT getApplicator(final ScriptApplicatorFactory<ApplicatorT> factory, final String resourcePath)
-    throws Exception {
-        final InputStream in = BaseScriptApplicatorRetreiver.class.getResourceAsStream(resourcePath);
-        if (null == in) {
-            return null;
-        }
-        try {
-            return factory.createScriptApplicator(in);
-        } finally {
-            in.close();
-        }
-    }
 }

src/main/java/org/nrg/net/xnat/DicomScriptApplicatorRetriever.java

  */
 package org.nrg.net.xnat;
 
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
+import org.nrg.dcm.edit.ScriptApplicator;
+import org.nrg.dcm.edit.ScriptEvaluationException;
+import org.nrg.dcm.edit.ScriptFunction;
+import org.nrg.dcm.edit.Variable;
+import org.nrg.net.RestServer;
+import org.nrg.net.xnat.BaseScriptApplicatorRetreiver.ScriptApplicatorFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
-import org.nrg.dcm.edit.ScriptApplicator;
-import org.nrg.dcm.edit.ScriptEvaluationException;
-import org.nrg.dcm.edit.ScriptFunction;
-import org.nrg.dcm.edit.Variable;
-import org.nrg.net.RestServer;
-import org.nrg.net.xnat.BaseScriptApplicatorRetreiver.ConnectionProcessor;
-import org.nrg.net.xnat.BaseScriptApplicatorRetreiver.ScriptApplicatorFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.Lists;
-
 public final class DicomScriptApplicatorRetriever
 implements Callable<Iterable<ScriptApplicator>> {
-    private static final String sitePath = "/data/config/anon/script?contents=true";
 
     private final Logger logger = LoggerFactory.getLogger(DicomScriptApplicatorRetriever.class);
     private final RestServer xnat;
     private final String project, projBasePath;
     private final ScriptApplicatorFactory<ScriptApplicator> factory;
 
-    public DicomScriptApplicatorRetriever(final RestServer xnat, final String project,
-            final Map<String, ScriptFunction> scriptFunctions) {
+    public DicomScriptApplicatorRetriever(final RestServer xnat, final String project, final Map<String, ScriptFunction> scriptFunctions) {
         this.xnat = xnat;
         this.project = project;
         this.projBasePath = "/data/config/edit/projects/" + project + "/image/dicom/";
         };
     }
 
-
     /*
      * (non-Javadoc)
      * @see java.util.concurrent.Callable#call()
     public final Iterable<ScriptApplicator> call() throws Exception {
         final List<ScriptApplicator> applicators = Lists.newArrayList();
 
-        // The site script is straightforward.
-        final ConnectionProcessor<ScriptApplicator> textProcessor = new ConnectionProcessor<ScriptApplicator>(factory);
-        xnat.doGet(sitePath, textProcessor);
-        final ScriptApplicator siteScript = textProcessor.getApplicator();
-        if (null != siteScript && !siteScript.getStatements().isEmpty()) {
-            applicators.add(siteScript);
+        if (xnat.isSiteWideAnonScriptEnabled() && !StringUtils.isBlank(xnat.getSiteWideAnonScript())) {
+            // The site script is straightforward.
+            final ScriptApplicator siteScript = new ScriptApplicator(xnat.getSiteWideAnonScriptAsStream());
+            if (null != siteScript && !siteScript.getStatements().isEmpty()) {
+                applicators.add(siteScript);
+            }
         }
 
         // The project scripts are sort of complicated.

src/main/java/org/nrg/net/xnat/PETTracerRetriever.java

         }
     }
 
-    public static final Set<String> getDefaultTracers() {
+    public static Set<String> getDefaultTracers() {
         return Sets.newLinkedHashSet(defaultTracers);
     }
 
-    private static final Set<String> getDefaultTracers(final String resource) {
+    private static Set<String> getDefaultTracers(final String resource) {
+        IOException ioexception = null;
+        final InputStream in = PETTracerRetriever.class.getResourceAsStream(resource);
+        if (null == in) {
+            throw new RuntimeException("Unable to load default PET tracers");
+        }
         try {
-            final InputStream in = PETTracerRetriever.class.getResourceAsStream(resource);
-            if (null == in) {
-                throw new RuntimeException("Unable to load default PET tracers");
+            return Sets.newLinkedHashSet(StringListConnectionProcessor.readStrings(in));
+        } catch (IOException e) {
+            ioexception = e;
+        } finally {
+            try {
+                in.close();
+            } catch (IOException e) {
+                ioexception = e;
             }
-            IOException ioexception = null;
-            try {
-                return Sets.newLinkedHashSet(StringListConnectionProcessor.readStrings(in));
-            } catch (IOException e) {
-                throw ioexception = e;
-            } finally {
-                try {
-                    in.close();
-                } catch (IOException e) {
-                    throw null == ioexception ? e : ioexception;
-                }
-            }
-        } catch (IOException e) {
-            throw new RuntimeException("Unable to read default PET tracers", e);
         }
+        throw new RuntimeException("Unable to read default PET tracers", ioexception);
     }
 }

src/main/java/org/nrg/net/xnat/SeriesImportFilterApplicatorRetriever.java

 
 import org.apache.commons.lang.StringUtils;
 import org.json.JSONException;
-import org.nrg.dcm.Series;
 import org.nrg.net.HttpException;
 import org.nrg.net.RestServer;
 
         }
 	}
 
-    public boolean checkSeries(Series series) {
-        final String description = series.getDescription();
-        return checkSeries(description);
-    }
-
     public boolean checkSeries(final String description) {
         return _siteWideFilters.allow(description) && (_projectFilters == null || _projectFilters.allow(description));
     }
 
     private SeriesImportFilter extractSeriesImportFilters(final String path) throws IOException, JSONException {
         try {
-            Map<String, String> keys = _xnat.getSeriesImportFilter(path);
+            Map<String, Object> keys = _xnat.getSeriesImportFilter(path);
             return new SeriesImportFilter(keys);
         } catch (HttpException exception) {
             // If it's a 404, that's totally cool, it can not exist.
             _mode = null;
         }
 
-        public SeriesImportFilter(final Map<String, String> keys) {
+        public SeriesImportFilter(final Map<String, Object> keys) {
             _enabled = keys.get("status").equals("enabled");
             if (!_enabled) {
                 _mode = "blacklist";
-            } else {
-                _mode = keys.get("mode");
-                final String rawFilters = keys.get("list");
+            } else if (keys.containsKey("contents")) {
+                @SuppressWarnings("unchecked")
+                Map<String, String> contents = (Map<String, String>) keys.get("contents");
+                _mode = contents.containsKey("mode") ? contents.get("mode") : "";
+                final String rawFilters = contents.containsKey("list") ? contents.get("list") : "";
                 if (!StringUtils.isBlank(rawFilters)) {
                     final String[] filters = rawFilters.trim().split("\n");
                     for (final String filter : filters) {
                         _patterns.add(Pattern.compile(filter, Pattern.CASE_INSENSITIVE));
                     }
                 }
+            } else {
+                _mode = "blacklist";
             }
         }