Java tutorial
/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is part of dcm4che, an implementation of DICOM(TM) in * Java(TM), hosted at https://github.com/gunterze/dcm4che. * * The Initial Developer of the Original Code is * Agfa Healthcare. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * See @authors listed below * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.dcm4che3.tool.qc; import org.apache.commons.cli.*; import org.dcm4che3.data.Attributes; import org.dcm4che3.data.Code; import org.dcm4che3.data.IDWithIssuer; import org.dcm4che3.data.Issuer; import org.dcm4che3.json.JSONWriter; import org.dcm4che3.tool.common.CLIUtils; import org.dcm4che3.tool.qc.test.QCResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.json.*; import javax.json.stream.JsonGenerator; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.ResourceBundle; /** * @author Hesham Elbadawi <bsdreko@gmail.com> * */ public class QC { static final Logger LOG = LoggerFactory.getLogger(QC.class); private static ResourceBundle rb = ResourceBundle.getBundle("org.dcm4che3.tool.qc.messages"); private static Options opts; private Code qcRejectionCode; private QCOperation operation; private String targetStudyUID; private String deleteParams; private String studyToDelete; private String seriesToDelete; private String instanceToDelete; private String url; private QCUpdateScope updateScope = QCUpdateScope.NONE; private Attributes targetStudyAttrs = new Attributes(); private Attributes targetSeriesAttrs = new Attributes(); private Attributes targetPatientAttributes = new Attributes(); private Attributes sourcePatientAttributes = new Attributes(); private Attributes updateAttrs = new Attributes(); private IDWithIssuer pid; private ArrayList<String> moveUIDs; private ArrayList<String> cloneUIDs; private ArrayList<String> mergeUIDs; private ArrayList<String> rrUIDs; public QC() { } public QC(String url, Code qcRejectionCode, QCOperation operation) { this.operation = operation; this.qcRejectionCode = qcRejectionCode; this.url = url; } public static void main(String[] args) { CommandLine cl = null; try { cl = parseComandLine(args); QC qc = initArgs(cl); initializeOptions(cl, qc); QCResult result = performOperation("Standard CLI tool Call", qc); printResult(result); } catch (Exception e) { System.err.println("qc: " + e.getMessage()); System.err.println(rb.getString("try")); System.exit(2); } } private static void printResult(QCResult result) { System.out.println("Server Responsed [" + result.getResponseCode() + "]" + ": \n" + " with the following message: \n" + result.getResponseMessage()); } public static QCResult performOperation(String opDescription, QC qc) throws IllegalArgumentException { switch (qc.getOperation()) { case SPLIT: case MERGE: case SEGMENT: case UPDATE: case REJECT: case RESTORE: JsonObject qcMessage = initQCObject(qc); System.out.println(qcMessage.toString()); return sendRequest(opDescription, qc, qcMessage); case DELETE: if (checkDelete(qc)) { return sendDeleteRequest(opDescription, qc); } case PATIENT_LINK: case PATIENT_MERGE: case PATIENT_UNLINK: case PATIENT_UPDATE_ID: JsonArray qcPatientMessage = initPatientObject(qc); qc.setUrl(adjustPatientURL(qc).replace(" ", "%20")); return sendRequest(opDescription, qc, qcPatientMessage); default: throw new IllegalArgumentException("Incorrect Operation specified"); } } private static boolean checkDelete(QC qc) { if (qc.getPid() != null) { return true; } String[] components = qc.getDeleteParams().split(":"); int idx = 0; for (String str : components) { switch (idx) { case 0: qc.setStudyToDelete(str); break; case 1: qc.setSeriesToDelete(str); break; case 2: qc.setInstanceToDelete(str); break; default: return false; } idx++; } return true; } private static QCResult sendDeleteRequest(String desc, QC qc) { HttpURLConnection connection = null; String bfr = ""; QCResult result = null; try { URL url = new URL(adjustDeleteURL(qc).replace(" ", "%20")); connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setInstanceFollowRedirects(false); connection.setRequestMethod("DELETE"); int responseCode = connection.getResponseCode(); if (responseCode == 200) { InputStream in = connection.getInputStream(); BufferedReader rdr = new BufferedReader(new InputStreamReader(in)); while (rdr.ready()) bfr += rdr.readLine(); } result = new QCResult(desc, bfr, responseCode); return result; } catch (Exception e) { System.out.println("Error preparing request or " + "parsing Server response - " + e); } return result; } private static String adjustPatientURL(QC qc) { String url = qc.getUrl(); return url += "/patients/" + qc.getOperation().toString().split("_")[1].toLowerCase(); } private static String adjustDeleteURL(QC qc) { String url = qc.getUrl(); if (qc.getPid() != null) { url += "/delete/patient/" + qc.getPid().getID(); return url += qc.getPid().getIssuer() != null ? "/issuer/" + qc.getPid().getIssuer().toString(':') : ""; } url += "/delete/studies/" + qc.getStudyToDelete(); if (qc.getSeriesToDelete() != null) url += "/series/" + qc.getSeriesToDelete(); if (qc.getInstanceToDelete() != null) url += "/instances/" + qc.getInstanceToDelete(); return url; } private static QCResult sendRequest(String desc, QC qc, Object qcMessage) { HttpURLConnection connection = null; String bfr = ""; QCResult result = null; try { URL url = new URL(qc.getUrl()); connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setInstanceFollowRedirects(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("charset", "utf-8"); connection.setRequestProperty("Accept", "application/json"); connection.setUseCaches(false); writeMessage(connection, (JsonStructure) qcMessage); InputStream in = connection.getInputStream(); BufferedReader rdr = new BufferedReader(new InputStreamReader(in)); while (rdr.ready()) bfr += rdr.readLine(); result = new QCResult(desc, bfr, connection.getResponseCode()); return result; } catch (Exception e) { System.out.println("Error preparing request or " + "parsing Server response - " + e); } connection.disconnect(); return result; } private static void writeMessage(HttpURLConnection connection, JsonStructure qcMessage) throws Exception { DataOutputStream wr; wr = new DataOutputStream(connection.getOutputStream()); // wr.writeBytes("Content-Type: application/json" + " \r\n"); // wr.writeBytes("\r\n"); JsonWriter writer = Json.createWriter(wr); if (qcMessage instanceof JsonObject) writer.writeObject((JsonObject) qcMessage); else writer.writeArray((JsonArray) qcMessage); writer.close(); wr.close(); } private static JsonObject initQCObject(QC qc) { mergeUIDs(qc); JsonObjectBuilder builder = Json.createObjectBuilder().add("operation", qc.getOperation().toString()) .add("updateScope", qc.getUpdateScope() == null ? QCUpdateScope.NONE.toString() : qc.getUpdateScope().toString()) .add("moveSOPUIDs", toArrayBuilder(qc.getMoveUIDs())) .add("cloneSOPUIDs", toArrayBuilder(qc.getCloneUIDs())) .add("restoreOrRejectUIDs", toArrayBuilder(qc.getRrUIDs())) .add("targetStudyUID", qc.getTargetStudyUID()) .add("qcRejectionCode", toCodeObject(qc.getQcRejectionCode())) .add("targetSeriesData", toAttributesObject(qc.getTargetSeriesAttrs())) .add("targetStudyData", toAttributesObject(qc.getTargetStudyAttrs())) .add("updateData", toAttributesObject(qc.getUpdateAttrs())); if (qc.getPid() != null) builder.add("pid", toIDWithIssuerObject(qc.getPid())); return builder.build(); } private static JsonArray initPatientObject(QC qc) { JsonArrayBuilder builder = Json.createArrayBuilder() .add(toAttributesObject(qc.getSourcePatientAttributes())) .add(toAttributesObject(qc.getTargetPatientAttributes())); return builder.build(); } protected static void mergeUIDs(QC qc) { if (qc.getOperation() == QCOperation.MERGE) { ArrayList<String> tmpMove = qc.getMoveUIDs() == null ? new ArrayList<String>() : qc.getMoveUIDs(); tmpMove.addAll(qc.getMergeUIDs()); qc.setMoveUIDs(tmpMove); } } private static JsonObject toIDWithIssuerObject(IDWithIssuer pid) { return Json.createObjectBuilder().add("id", emptyIfNull(pid.getID())) .add("issuer", toIssuerObject(pid.getIssuer())).build(); } private static JsonObject toIssuerObject(Issuer issuer) { JsonObjectBuilder builder = Json.createObjectBuilder().add("localNamespaceEntityID", emptyIfNull(issuer.getLocalNamespaceEntityID())); if (issuer.getUniversalEntityID() != null) builder.add("universalEntityID", emptyIfNull(issuer.getUniversalEntityID())) .add("universalEntityIDType", emptyIfNull(issuer.getUniversalEntityIDType())); return builder.build(); } private static JsonObject toAttributesObject(Attributes targetSeriesAttrs) { StringWriter strWriter = new StringWriter(); JsonGenerator gen = Json.createGenerator(strWriter); JSONWriter writer = new JSONWriter(gen); writer.write(targetSeriesAttrs); gen.flush(); gen.close(); return Json.createReader(new StringReader(strWriter.toString())).readObject(); } private static JsonObject toCodeObject(Code qcRejectionCode) { JsonObjectBuilder codeBuilder = Json.createObjectBuilder() .add("codeValue", emptyIfNull(qcRejectionCode.getCodeValue())) .add("codeMeaning", emptyIfNull(qcRejectionCode.getCodeMeaning())) .add("codingSchemeDesignator", emptyIfNull(qcRejectionCode.getCodingSchemeDesignator())); if (qcRejectionCode.getCodingSchemeVersion() != null) codeBuilder.add("codingSchemeVersion", qcRejectionCode.getCodingSchemeVersion()); return codeBuilder.build(); } private static String emptyIfNull(String obj) { return obj == null ? "" : obj; } private static JsonArrayBuilder toArrayBuilder(ArrayList<String> moveUIDs) { JsonArrayBuilder arr = Json.createArrayBuilder(); for (String str : moveUIDs) arr.add(str); return arr; } private static ArrayList<String> toUIDS(String optionValue) { ArrayList<String> uids = new ArrayList<String>(); for (String str : optionValue.split(",")) { uids.add(str); } return uids; } public static IDWithIssuer toIDWithIssuer(String optionValue) throws MissingArgumentException { String[] components = optionValue.split(":"); if (components.length < 2) throw new MissingArgumentException("Missing issuer information"); if (components.length == 2) // pid and local entity uid return new IDWithIssuer(components[0], components[1]); else if (components.length == 3) // pid with universal entity and type return new IDWithIssuer(components[0], new Issuer(null, components[1], components[2])); else return new IDWithIssuer(components[0], new Issuer(components[1], components[2], components[3])); } private static Code toCode(String codeComponents) throws MissingArgumentException { String[] components = codeComponents.split(":"); if (components.length < 3) throw new MissingArgumentException("Invalid code specified " + " code can be specified as codevalue" + ":codeschemedesignator" + ":codemeaning:codeversion where only " + "version is optional"); return new Code(components[0], components[2], components.length == 4 ? components[3] : null, components[1]); } @SuppressWarnings("static-access") private static CommandLine parseComandLine(String[] args) throws ParseException { opts = new Options(); opts.addOption(OptionBuilder.hasArgs().withArgName("[seq/]attr=value").withValueSeparator('=') .withDescription(rb.getString("updateattributes")).create("updateattributes")); opts.addOption(OptionBuilder.hasArgs().withArgName("[seq/]attr=value").withValueSeparator('=') .withDescription(rb.getString("targetpatientattributes")).create("targetpatientattributes")); opts.addOption(OptionBuilder.hasArgs().withArgName("[seq/]attr=value").withValueSeparator('=') .withDescription(rb.getString("sourcepatientattributes")).create("sourcepatientattributes")); CLIUtils.addCommonOptions(opts); opts.addOption(OptionBuilder.hasArgs().withArgName("[seq/]attr=value").withValueSeparator('=') .withDescription(rb.getString("overrideseriesattributes")).create("overrideseriesattributes")); opts.addOption(OptionBuilder.hasArgs().withArgName("[seq/]attr=value").withValueSeparator('=') .withDescription(rb.getString("overridestudyattributes")).create("overridestudyattributes")); opts.addOption(OptionBuilder.hasArg().withDescription(rb.getString("cloneuids")).create("cloneuids")); opts.addOption(OptionBuilder.hasArg().withDescription(rb.getString("moveuids")).create("moveuids")); opts.addOption( OptionBuilder.hasArg().withDescription(rb.getString("mergestudyuids")).create("mergestudyuids")); opts.addOption(OptionBuilder.hasArg().withDescription(rb.getString("restorerejectuids")) .create("restorerejectuids")); opts.addOption(OptionBuilder.hasArg().withDescription(rb.getString("pid")).create("pid")); opts.addOption(OptionBuilder.hasArg().withDescription(rb.getString("updatescope")) .withArgName("<STUDY|SERIES|PATIENT|INSTANCE>").create("updatescope")); opts.addOption(OptionBuilder.hasArg().withDescription(rb.getString("deleteobject")).create("deleteobject")); return CLIUtils.parseComandLine(args, opts, rb, QC.class); } private static QC initArgs(CommandLine cl) throws MissingArgumentException, IndexOutOfBoundsException { String codeComponents = null; String parsedURL = (String) cl.getArgList().get(0); String operation = (String) cl.getArgList().get(1); if (cl.getArgList().size() > 2) codeComponents = (String) cl.getArgList().get(2); QC qc = new QC(parsedURL, codeComponents != null ? toCode(codeComponents) : null, QCOperation.valueOf(operation.toUpperCase())); if (cl.getArgList().size() > 3) { qc.setTargetStudyUID((String) cl.getArgList().get(3)); } return qc; } private static void initializeOptions(CommandLine cl, QC qc) throws MissingArgumentException { if (qc.operation == QCOperation.UPDATE) if (!cl.hasOption("update-scope")) throw new MissingArgumentException( "Missing required argument" + " update scope for update operation"); else qc.setUpdateScope(QCUpdateScope.valueOf(cl.getOptionValue("update-scope"))); if (cl.hasOption("overridestudyattributes")) qc.targetStudyAttrs = getAttributes(cl, "overridestudyattributes"); if (cl.hasOption("overrideseriesattributes")) qc.setTargetSeriesAttrs(getAttributes(cl, "overrideseriesattributes")); if (cl.hasOption("updateattributes")) qc.setUpdateAttrs(getAttributes(cl, "updateattributes")); if (cl.hasOption("targetpatientattributes")) qc.setTargetPatientAttributes(getAttributes(cl, "targetpatientattributes")); if (cl.hasOption("sourcepatientattributes")) qc.setSourcePatientAttributes(getAttributes(cl, "sourcepatientattributes")); if (cl.hasOption("pid")) qc.setPid(toIDWithIssuer(cl.getOptionValue("pid"))); if (cl.hasOption("moveuids")) qc.setMoveUIDs(toUIDS(cl.getOptionValue("moveuids"))); if (cl.hasOption("cloneuids")) qc.setCloneUIDs(toUIDS(cl.getOptionValue("cloneuids"))); if (cl.hasOption("restorerejectuids")) qc.setRrUIDs(toUIDS(cl.getOptionValue("restorerejectuids"))); if (cl.hasOption("mergestudyuids")) qc.setMergeUIDs(toUIDS(cl.getOptionValue("mergeuids"))); if (cl.hasOption("deleteobject") && qc.getOperation() == QCOperation.DELETE) qc.setDeleteParams(cl.getOptionValue("deleteobject")); } private static Attributes getAttributes(CommandLine cl, String optionName) { Attributes temp = new Attributes(); CLIUtils.addAttributes(temp, cl.getOptionValues(optionName)); return temp; } public Code getQcRejectionCode() { return qcRejectionCode; } public QCOperation getOperation() { return operation; } public String getTargetStudyUID() { return targetStudyUID; } public String getUrl() { return url; } public Attributes getTargetStudyAttrs() { return targetStudyAttrs; } public Attributes getTargetSeriesAttrs() { return targetSeriesAttrs; } public Attributes getUpdateAttrs() { return updateAttrs; } public IDWithIssuer getPid() { return pid; } public ArrayList<String> getMoveUIDs() { return moveUIDs == null ? new ArrayList<String>() : moveUIDs; } public ArrayList<String> getCloneUIDs() { return cloneUIDs == null ? new ArrayList<String>() : cloneUIDs; } public ArrayList<String> getMergeUIDs() { return mergeUIDs == null ? new ArrayList<String>() : mergeUIDs; } public ArrayList<String> getRrUIDs() { return rrUIDs == null ? new ArrayList<String>() : rrUIDs; } public void setQcRejectionCode(Code qcRejectionCode) { this.qcRejectionCode = qcRejectionCode; } public void setOperation(QCOperation operation) { this.operation = operation; } public void setTargetStudyUID(String targetStudyUID) { this.targetStudyUID = targetStudyUID; } public void setUrl(String url) { this.url = url; } public void setTargetStudyAttrs(Attributes targetStudyAttrs) { this.targetStudyAttrs = targetStudyAttrs; } public void setTargetSeriesAttrs(Attributes targetSeriesAttrs) { this.targetSeriesAttrs = targetSeriesAttrs; } public void setUpdateAttrs(Attributes updateAttrs) { this.updateAttrs = updateAttrs; } public void setPid(IDWithIssuer pid) { this.pid = pid; } public void setMoveUIDs(ArrayList<String> moveUIDs) { this.moveUIDs = moveUIDs; } public void setCloneUIDs(ArrayList<String> cloneUIDs) { this.cloneUIDs = cloneUIDs; } public void setMergeUIDs(ArrayList<String> mergeUIDs) { this.mergeUIDs = mergeUIDs; } public void setRrUIDs(ArrayList<String> rrUIDs) { this.rrUIDs = rrUIDs; } public QCUpdateScope getUpdateScope() { return updateScope; } public void setUpdateScope(QCUpdateScope updateScope) { this.updateScope = updateScope; } public String getDeleteParams() { return deleteParams; } public void setDeleteParams(String deleteParams) { this.deleteParams = deleteParams; } public String getStudyToDelete() { return studyToDelete; } public void setStudyToDelete(String studyToDelete) { this.studyToDelete = studyToDelete; } public String getSeriesToDelete() { return seriesToDelete; } public void setSeriesToDelete(String seriesToDelete) { this.seriesToDelete = seriesToDelete; } public String getInstanceToDelete() { return instanceToDelete; } public void setInstanceToDelete(String instanceToDelete) { this.instanceToDelete = instanceToDelete; } public Attributes getTargetPatientAttributes() { return targetPatientAttributes; } public void setTargetPatientAttributes(Attributes targetPatientAttributes) { this.targetPatientAttributes = targetPatientAttributes; } public Attributes getSourcePatientAttributes() { return sourcePatientAttributes; } public void setSourcePatientAttributes(Attributes sourcePatientAttributes) { this.sourcePatientAttributes = sourcePatientAttributes; } }