private void echo(ActiveAssociation aa, int nrOfTests) throws Exception {
   AssociationFactory aFact = AssociationFactory.getInstance();
   DcmObjectFactory oFact = DcmObjectFactory.getInstance();
   for (int i = 0; i < nrOfTests; i++) {
     aa.invoke(aFact.newDimse(PCID_ECHO, oFact.newCommand().initCEchoRQ(i)), null);
   }
 }
  private Collection getObserverContextItems(String personName) {
    Dataset ds = dof.newDataset();
    ds.putCS(Tags.RelationshipType, "HAS OBS CONTEXT");
    ds.putCS(Tags.ValueType, "CODE");
    DcmElement cnSq = ds.putSQ(Tags.ConceptNameCodeSeq);
    Dataset cnDS = cnSq.addNewItem();
    cnDS.putSH(Tags.CodeValue, "121005");
    cnDS.putSH(Tags.CodingSchemeDesignator, "DCM");
    cnDS.putLO(Tags.CodeMeaning, "ObserverType");
    DcmElement ccSq = ds.putSQ(Tags.ConceptCodeSeq);
    Dataset ccDS = ccSq.addNewItem();
    ccDS.putSH(Tags.CodeValue, "121006");
    ccDS.putSH(Tags.CodingSchemeDesignator, "DCM");
    ccDS.putLO(Tags.CodeMeaning, "Person");

    Dataset ds1 = dof.newDataset();
    ds1.putCS(Tags.RelationshipType, "HAS OBS CONTEXT");
    ds1.putCS(Tags.ValueType, "PNAME");
    DcmElement cnSq1 = ds1.putSQ(Tags.ConceptNameCodeSeq);
    Dataset cnDS1 = cnSq1.addNewItem();
    cnDS1.putSH(Tags.CodeValue, "121008");
    cnDS1.putSH(Tags.CodingSchemeDesignator, "DCM");
    cnDS1.putLO(Tags.CodeMeaning, "Person Observer Name");
    ds1.putPN(Tags.PersonName, personName);
    ArrayList col = new ArrayList();
    col.add(ds);
    col.add(ds1);
    return col;
  }
  /**
   * Prepares the Dataset representing the search key in C-FIND. As no values are set, the keys
   * match to every content in the archive. The user has to specify concret values to limit the
   * searchSee PS 3.4 - C.6.2.1.2 Study level.
   *
   * <p>As the result for C-FIND these keys are filled with the values found in the archive.
   *
   * @param cfg the configuration properties for this class.
   * @throws ParseException if a given properties for the keys was not found.
   */
  private void initKeys(ConfigProperties cfg) throws ParseException {
    // Remove all keys
    keys = dof.newDataset();

    // Query/Retrieve Level. PS 3.4 - C.6.2 Study Root SOP Class Group
    keys.putCS(Tags.QueryRetrieveLevel, getQueryRetrieveLevel(STUDY_LEVEL));

    // UNIQUE STUDY LEVEL KEY FOR THE STUDY. See PS 3.4 - C.6.2.1.2 Study level
    keys.putUI(Tags.StudyInstanceUID);

    // REQUIRED STUDY LEVEL KEY FOR THE STUDY. See PS 3.4 - C.6.2.1.2 Study level
    keys.putDA(Tags.StudyDate);
    // Not defined: StudyTime
    // Not defined: AccessionNumber
    keys.putPN(Tags.PatientName);
    keys.putLO(Tags.PatientID);
    // Not defined: StudyID

    // OPTIONAL STUDY LEVEL KEY FOR THE STUDY. See PS 3.4 - C.6.2.1.2 Study level
    keys.putUS(Tags.NumberOfStudyRelatedSeries);
    keys.putUS(Tags.NumberOfStudyRelatedInstances);
    // mutch more defined...

    // Add the keys found in the configuration properties
    addQueryKeys(cfg);
  }
 private Map string2Codes(String codes, String defaultDesign) {
   StringTokenizer st = new StringTokenizer(codes, ",");
   Map map = new HashMap();
   int nrOfTokens;
   StringTokenizer stCode;
   Dataset ds;
   String codeValue;
   while (st.hasMoreTokens()) {
     stCode = new StringTokenizer(st.nextToken(), "^");
     nrOfTokens = stCode.countTokens();
     if (nrOfTokens < 2) {
       throw new IllegalArgumentException(
           "Wrong format of human performer configuration! (<codeValue>[^<designator>]^<meaning>)");
     }
     ds = dof.newDataset();
     codeValue = stCode.nextToken();
     ds.putSH(Tags.CodeValue, codeValue);
     if (nrOfTokens > 2) {
       ds.putSH(Tags.CodingSchemeDesignator, stCode.nextToken());
     } else if (defaultDesign != null) {
       ds.putSH(Tags.CodingSchemeDesignator, defaultDesign);
     }
     ds.putLO(Tags.CodeMeaning, stCode.nextToken());
     map.put(codeValue, ds);
   }
   return map;
 }
  /**
   * Sets the Dataset representing the query keys in C-FIND. The user can specify concret values to
   * limit the search.
   *
   * <p>Wildcards '*','?', '-' and '\' are allowed as element-values (see PS 3.4 - C.2.2.2 Attribute
   * Matching). If an key attribute is defined, but has an empty value, it matches every value at
   * the storage side.
   *
   * <p>At a minimum the key "QueryRetrieveLevel" must be set ("STUDY", "SERIES" or "IMAGE"). See PS
   * 3.4 - C.6.2 Study Root SOP Class Group
   *
   * <p>As the result for C-FIND these keys are filled with the values found in the archive.
   *
   * @param cfg the configuration properties for this class.
   * @throws ParseException if a given properties for the keys was not found.
   */
  public void setQueryKeys(ConfigProperties cfg) throws ParseException {

    // Remove all keys
    keys = dof.newDataset();

    // Add the keys found in the configuration properties
    addQueryKeys(cfg);
  }
 /**
  * @param xdsiModel
  * @return
  */
 private Dataset getRootInfo(XDSIModel xdsiModel) {
   Dataset rootInfo = DcmObjectFactory.getInstance().newDataset();
   DcmElement sq = rootInfo.putSQ(Tags.ConceptNameCodeSeq);
   Dataset item = sq.addNewItem();
   CodeItem selectedDocTitle = xdsiModel.selectedDocTitle();
   item.putSH(Tags.CodeValue, selectedDocTitle.getCodeValue());
   item.putSH(Tags.CodingSchemeDesignator, selectedDocTitle.getCodeDesignator());
   item.putLO(Tags.CodeMeaning, selectedDocTitle.getCodeMeaning());
   return rootInfo;
 }
 private PersonName newPersonName(String pname) {
   try {
     return DcmObjectFactory.getInstance().newPersonName(pname);
   } catch (IllegalArgumentException e) {
     LOG.warn(
         "Cannot generate Soundex code for Illegal Person Name: "
             + pname
             + " - treat as unknown.");
     return null;
   }
 }
Exemple #8
0
 public static boolean equalsPixelData(File f1, File f2) throws IOException {
   InputStream in1 = new BufferedInputStream(new FileInputStream(f1));
   try {
     InputStream in2 = new BufferedInputStream(new FileInputStream(f2));
     try {
       Dataset attrs = DcmObjectFactory.getInstance().newDataset();
       DcmParserFactory pf = DcmParserFactory.getInstance();
       DcmParser p1 = pf.newDcmParser(in1);
       DcmParser p2 = pf.newDcmParser(in2);
       p1.setDcmHandler(attrs.getDcmHandler());
       p1.parseDcmFile(FileFormat.DICOM_FILE, Tags.PixelData);
       p2.parseDcmFile(FileFormat.DICOM_FILE, Tags.PixelData);
       int samples = attrs.getInt(Tags.SamplesPerPixel, 1);
       int frames = attrs.getInt(Tags.NumberOfFrames, 1);
       int rows = attrs.getInt(Tags.Rows, 1);
       int columns = attrs.getInt(Tags.Columns, 1);
       int bitsAlloc = attrs.getInt(Tags.BitsAllocated, 8);
       int bitsStored = attrs.getInt(Tags.BitsStored, bitsAlloc);
       int frameLength = rows * columns * samples * bitsAlloc / 8;
       int pixelDataLength = frameLength * frames;
       if (pixelDataLength > p1.getReadLength() || pixelDataLength > p2.getReadLength()) {
         return false;
       }
       byte[] b1 = new byte[BUFFER_SIZE];
       byte[] b2 = new byte[BUFFER_SIZE];
       int[] mask = {0xff, 0xff};
       int len, len2;
       if (bitsAlloc == 16 && bitsStored < 16) {
         mask[p1.getDcmDecodeParam().byteOrder == ByteOrder.LITTLE_ENDIAN ? 1 : 0] =
             0xff >>> (16 - bitsStored);
       }
       int pos = 0;
       while (pos < pixelDataLength) {
         len = in1.read(b1, 0, Math.min(pixelDataLength - pos, BUFFER_SIZE));
         if (len < 0) // EOF
         return false;
         int off = 0;
         while (off < len) {
           off += len2 = in2.read(b2, off, len - off);
           if (len2 < 0) // EOF
           return false;
         }
         for (int i = 0; i < len; i++, pos++)
           if (((b1[i] - b2[i]) & mask[pos & 1]) != 0) return false;
       }
       return true;
     } finally {
       in2.close();
     }
   } finally {
     in1.close();
   }
 }
  /**
   * Implements the ECHO service. The C-ECHO service is invoked by a DIMSE-service-user to verify
   * end-to-end communications with a peer DIMSE-service-user. See PS 3.7 - 9.1.5 C-ECHO SERVICE
   *
   * @exception ConnectException
   * @exception InterruptedException
   * @exception IOException
   */
  public long cECHO() throws ConnectException, InterruptedException, IOException {
    PresContext pc;
    long t1 = System.currentTimeMillis();

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-ECHO is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    if ((pc =
            aassoc
                .getAssociation()
                .getAcceptedPresContext(UIDs.Verification, UIDs.ImplicitVRLittleEndian))
        == null) {
      throw new ConnectException(
          "Association does not support presentation context: Verification SOP/ImplicitVRLittleEndian.");
    }

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command cEchoRQ = oFact.newCommand();
    // API doc: Command.initCEchoRQ(int msgID)
    cEchoRQ.initCEchoRQ(1);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse echoRq = aFact.newDimse(pc.pcid(), cEchoRQ);

    // PS 3.7 - 9.3.5 C-ECHO PROTOCOL, 9.3.5.2 C-ECHO-RSP
    // Always returns SUCESS result code.
    // Invoke active association with echo request Dimse
    FutureRSP future = aassoc.invoke(echoRq);

    // Response to the C-ECHO request.
    // The result cannot be accessed until it has been set.
    Dimse echoRsp = future.get();
    Command rspCmd = echoRsp.getCommand();

    // PS 3.7 - 9.3.5 C-MOVE PROTOCOL, 9.3.5.2 C-ECHO-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // Success
        break;
      default:
        log.error("C-ECHO failed: " + Integer.toHexString(status));
        break;
    }

    return System.currentTimeMillis() - t1;
  }
Exemple #10
0
 void sendPPS(boolean create, Dataset pps, String aet) throws Exception {
   ActiveAssociation aa = openAssociation(aet, UIDs.GeneralPurposePerformedProcedureStepSOPClass);
   try {
     Association a = aa.getAssociation();
     DcmObjectFactory dof = DcmObjectFactory.getInstance();
     Command cmdRq = dof.newCommand();
     final String iuid = pps.getString(Tags.SOPInstanceUID);
     if (create) {
       cmdRq.initNCreateRQ(a.nextMsgID(), UIDs.GeneralPurposePerformedProcedureStepSOPClass, iuid);
     } else {
       cmdRq.initNSetRQ(a.nextMsgID(), UIDs.GeneralPurposePerformedProcedureStepSOPClass, iuid);
     }
     Dimse dimseRq =
         AssociationFactory.getInstance().newDimse(PCID_GPPPS, cmdRq, pps.exclude(SOP_IUID));
     if (log.isDebugEnabled()) {
       log.debug("GP-PPS Attributes:");
       log.debug(pps);
     }
     final Dimse dimseRsp = aa.invoke(dimseRq).get();
     final Command cmdRsp = dimseRsp.getCommand();
     final int status = cmdRsp.getStatus();
     switch (status) {
       case 0x0000:
         break;
       case 0x0116:
         log.warn(
             "Received Warning Status 116H (=Attribute Value Out of Range) from remote AE " + aet);
         break;
       default:
         throw new DcmServiceException(status, cmdRsp.getString(Tags.ErrorComment));
     }
   } finally {
     try {
       aa.release(true);
     } catch (Exception e) {
       log.warn("Failed to release " + aa.getAssociation());
     }
   }
 }
 private Dataset loadDataset(File file) throws IOException {
   BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
   Dataset ds = factory.newDataset();
   try {
     ds.readFile(bis, null, -1);
   } finally {
     try {
       bis.close();
     } catch (IOException ignore) {
     }
   }
   if (log.isDebugEnabled()) log.debug("Dataset for file " + file + " :" + ds);
   return ds;
 }
  /**
   * Imports a DICOM file.
   *
   * <p>The FileDTO object refers to an existing DICOM file (This method does NOT check this file!)
   * and the Dataset object holds the meta data for database.
   *
   * <p>
   *
   * @param fileDTO Refers the DICOM file.
   * @param ds Dataset with metadata for database.
   * @param last last file to import
   */
  public void importFile(FileDTO fileDTO, Dataset ds, String prevseriuid, boolean last)
      throws Exception {
    Storage store = getStorage();
    String seriud = ds.getString(Tags.SeriesInstanceUID);
    if (prevseriuid != null && !prevseriuid.equals(seriud)) {
      SeriesStored seriesStored = store.makeSeriesStored(prevseriuid);
      if (seriesStored != null) {
        log.debug("Send SeriesStoredNotification - series changed");
        scp.doAfterSeriesIsStored(store, null, seriesStored);
      }
    }
    String cuid = ds.getString(Tags.SOPClassUID);
    String iuid = ds.getString(Tags.SOPInstanceUID);
    FileMetaInfo fmi =
        DcmObjectFactory.getInstance().newFileMetaInfo(cuid, iuid, fileDTO.getFileTsuid());
    ds.setFileMetaInfo(fmi);
    String fsPath = fileDTO.getDirectoryPath();
    String filePath = fileDTO.getFilePath();
    File f = FileUtils.toFile(fsPath, filePath);

    // Modified by [email protected] on 01.09.2009
    // In case the file is not stored in file system but somewhere else
    //      scp.updateDB(store, ds, fileDTO.getFileSystemPk(), filePath, f.length(),
    //              fileDTO.getFileMd5(), true);
    scp.updateDB(
        store,
        ds,
        fileDTO.getFileSystemPk(),
        filePath,
        fileDTO.getFileSize(),
        fileDTO.getFileMd5(),
        true);
    if (last) {
      SeriesStored seriesStored = store.makeSeriesStored(seriud);
      if (seriesStored != null) {
        scp.doAfterSeriesIsStored(store, null, seriesStored);
      }
    }
  }
  private Dataset getStudyMgtDataset(StudyLocal study, Map mapSeries) {
    Dataset ds = dof.newDataset();
    ds.putUI(Tags.StudyInstanceUID, study.getStudyIuid());
    ds.putOB(PrivateTags.StudyPk, Convert.toBytes(study.getPk().longValue()));
    ds.putSH(Tags.AccessionNumber, study.getAccessionNumber());
    ds.putLO(Tags.PatientID, study.getPatient().getPatientId());
    ds.putLO(Tags.IssuerOfPatientID, study.getPatient().getIssuerOfPatientId());
    ds.putPN(Tags.PatientName, study.getPatient().getPatientName());

    log.debug("getStudyMgtDataset: studyIUID:" + study.getStudyIuid());
    DcmElement refSeriesSeq = ds.putSQ(Tags.RefSeriesSeq);

    Iterator iter =
        (mapSeries == null) ? study.getSeries().iterator() : mapSeries.keySet().iterator();
    while (iter.hasNext()) {
      SeriesLocal sl = (SeriesLocal) iter.next();
      Dataset dsSer = refSeriesSeq.addNewItem();
      dsSer.putUI(Tags.SeriesInstanceUID, sl.getSeriesIuid());
      Collection instances =
          (mapSeries == null) ? sl.getInstances() : (Collection) mapSeries.get(sl);
      Iterator iter2 = instances.iterator();
      DcmElement refSopSeq = null;
      if (iter2.hasNext()) refSopSeq = dsSer.putSQ(Tags.RefSOPSeq);
      while (iter2.hasNext()) {
        InstanceLocal il = (InstanceLocal) iter2.next();
        Dataset dsInst = refSopSeq.addNewItem();
        dsInst.putUI(Tags.RefSOPClassUID, il.getSopCuid());
        dsInst.putUI(Tags.RefSOPInstanceUID, il.getSopIuid());
        dsInst.putAE(Tags.RetrieveAET, il.getRetrieveAETs());
      }
    }
    if (log.isDebugEnabled()) {
      log.debug("return StgMgtDataset:");
      log.debug(ds);
    }
    return ds;
  }
/**
 * @author [email protected]
 * @version $Revision: 3345 $ $Date: 2007-05-18 10:53:26 +0200 (Fr, 18. Mai 2007) $
 * @since 05.04.2005
 */
public class GPWLFeedService extends ServiceMBeanSupport {

  private static final int[] PAT_ATTR_TAGS = {
    Tags.PatientName, Tags.PatientID, Tags.PatientBirthDate, Tags.PatientSex,
  };

  private Map humanPerformer = null;

  private List templates = null;

  private File templatePath = null;

  private static DcmObjectFactory dof = DcmObjectFactory.getInstance();

  /** @return Returns the physicians. */
  public String getHumanPerformer() {
    return codes2String(humanPerformer);
  }

  /** @param performer The human performer(s) to set. */
  public void setHumanPerformer(String performer) {
    this.humanPerformer = string2Codes(performer, "DCM4CHEE");
  }

  /** @return Returns the configURL. */
  public String getTemplatePath() {
    return templatePath.getPath();
  }

  /**
   * @param configURL The configURL to set.
   * @throws MalformedURLException
   */
  public void setTemplatePath(String path) throws MalformedURLException {
    templatePath = new File(path.replace('/', File.separatorChar));
  }

  private String codes2String(Map codes) {
    if (codes == null || codes.isEmpty()) return "";
    StringBuffer sb = new StringBuffer();
    Dataset ds;
    String design;
    for (Iterator iter = codes.values().iterator(); iter.hasNext(); ) {
      ds = (Dataset) iter.next();
      design = ds.getString(Tags.CodingSchemeDesignator);
      sb.append(ds.getString(Tags.CodeValue)).append("^");
      if (design != null) sb.append(design).append("^");
      sb.append(ds.getString(Tags.CodeMeaning)).append(",");
    }

    return sb.substring(0, sb.length() - 1);
  }

  private Map string2Codes(String codes, String defaultDesign) {
    StringTokenizer st = new StringTokenizer(codes, ",");
    Map map = new HashMap();
    int nrOfTokens;
    StringTokenizer stCode;
    Dataset ds;
    String codeValue;
    while (st.hasMoreTokens()) {
      stCode = new StringTokenizer(st.nextToken(), "^");
      nrOfTokens = stCode.countTokens();
      if (nrOfTokens < 2) {
        throw new IllegalArgumentException(
            "Wrong format of human performer configuration! (<codeValue>[^<designator>]^<meaning>)");
      }
      ds = dof.newDataset();
      codeValue = stCode.nextToken();
      ds.putSH(Tags.CodeValue, codeValue);
      if (nrOfTokens > 2) {
        ds.putSH(Tags.CodingSchemeDesignator, stCode.nextToken());
      } else if (defaultDesign != null) {
        ds.putSH(Tags.CodingSchemeDesignator, defaultDesign);
      }
      ds.putLO(Tags.CodeMeaning, stCode.nextToken());
      map.put(codeValue, ds);
    }
    return map;
  }

  public List listTemplates() {
    if (templates == null) {
      File tmplPath = FileUtils.resolve(templatePath);
      File[] files = tmplPath.listFiles();
      templates = new ArrayList();
      String fn;
      for (int i = 0; i < files.length; i++) {
        fn = files[i].getName();
        if (fn.endsWith(".xml")) {
          templates.add(fn.substring(0, fn.length() - 4));
        }
      }
    }
    log.info("Template List:" + templates);
    return templates;
  }

  public void clearTemplateList() {
    templates = null;
  }

  public void addWorklistItem(
      Long studyPk, String templateFile, String humanPerformerCode, Long scheduleDate)
      throws Exception {
    String uri =
        FileUtils.resolve(new File(templatePath, templateFile + ".xml")).toURI().toString();
    if (log.isDebugEnabled()) log.debug("load template file: " + uri);
    Dataset ds = DatasetUtils.fromXML(new InputSource(uri));

    ContentManager cm = getContentManager();
    // patient
    Dataset patDS = cm.getPatientForStudy(studyPk.longValue());
    if (log.isDebugEnabled()) {
      log.debug("Patient Dataset:");
      log.debug(patDS);
    }

    ds.putAll(patDS.subSet(PAT_ATTR_TAGS));
    //
    Dataset sopInstRef = cm.getSOPInstanceRefMacro(studyPk.longValue(), false);
    String studyIUID = sopInstRef.getString(Tags.StudyInstanceUID);
    ds.putUI(Tags.SOPInstanceUID, UIDGenerator.getInstance().createUID());
    ds.putUI(Tags.StudyInstanceUID, studyIUID);
    DcmElement inSq = ds.putSQ(Tags.InputInformationSeq);
    inSq.addItem(sopInstRef);

    // Scheduled Human Performer Seq
    DcmElement schedHPSq = ds.putSQ(Tags.ScheduledHumanPerformersSeq);
    Dataset item = schedHPSq.addNewItem();
    DcmElement hpCodeSq = item.putSQ(Tags.HumanPerformerCodeSeq);
    Dataset dsCode = (Dataset) this.humanPerformer.get(humanPerformerCode);
    log.info(dsCode);
    if (dsCode != null) {
      hpCodeSq.addItem(dsCode);
      item.putPN(Tags.HumanPerformerName, dsCode.getString(Tags.CodeMeaning));
    }

    // Scheduled Procedure Step Start Date and Time
    ds.putDT(Tags.SPSStartDateAndTime, new Date(scheduleDate.longValue()));

    if (log.isDebugEnabled()) {
      log.debug("GPSPS Dataset:");
      log.debug(ds);
    }

    addWorklistItem(ds);
  }

  private void addWorklistItem(Dataset ds) {
    if (ds == null) return;
    try {
      getGPWLManager().addWorklistItem(ds);
    } catch (Exception e) {
      log.error("Failed to add Worklist Item:", e);
    }
  }

  private GPWLManager getGPWLManager()
      throws CreateException, RemoteException, HomeFactoryException {
    return ((GPWLManagerHome)
            EJBHomeFactory.getFactory().lookup(GPWLManagerHome.class, GPWLManagerHome.JNDI_NAME))
        .create();
  }

  private ContentManager getContentManager() throws Exception {
    ContentManagerHome home =
        (ContentManagerHome)
            EJBHomeFactory.getFactory()
                .lookup(ContentManagerHome.class, ContentManagerHome.JNDI_NAME);
    return home.create();
  }
}
  /**
   * Convert an image to RGB.
   *
   * @param inFile the file to convert.
   * @param outFile the output file, which may be same as inFile.
   * @return the static status result
   */
  public static AnonymizerStatus convert(File inFile, File outFile) {

    long fileLength = inFile.length();
    logger.debug("Entering DICOMPaletteImageConverter.convert");
    logger.debug("File length       = " + fileLength);

    BufferedInputStream in = null;
    BufferedOutputStream out = null;
    File tempFile = null;
    byte[] buffer = new byte[4096];
    try {
      // Check that this is a known format.
      in = new BufferedInputStream(new FileInputStream(inFile));
      DcmParser parser = pFact.newDcmParser(in);
      FileFormat fileFormat = parser.detectFileFormat();
      if (fileFormat == null) {
        throw new IOException("Unrecognized file format: " + inFile);
      }

      // Get the dataset (excluding pixels) and leave the input stream open
      Dataset dataset = oFact.newDataset();
      parser.setDcmHandler(dataset.getDcmHandler());
      parser.parseDcmFile(fileFormat, Tags.PixelData);

      // Make sure this is an image
      if (parser.getReadTag() != Tags.PixelData) {
        close(in);
        return AnonymizerStatus.SKIP(inFile, "Not an image");
      }

      // Get the required parameters and make sure they are okay
      int numberOfFrames = getInt(dataset, Tags.NumberOfFrames, 1);
      int rows = getInt(dataset, Tags.Rows, 0);
      int columns = getInt(dataset, Tags.Columns, 0);
      String photometricInterpretation = getString(dataset, Tags.PhotometricInterpretation, "");
      if ((rows == 0) || (columns == 0)) {
        close(in);
        return AnonymizerStatus.SKIP(inFile, "Unable to get the rows and columns");
      }
      if (!photometricInterpretation.equals("PALETTE COLOR")) {
        close(in);
        return AnonymizerStatus.SKIP(
            inFile, "Unsupported PhotometricInterpretation: " + photometricInterpretation);
      }
      if (parser.getReadTag() != Tags.PixelData) {
        close(in);
        return AnonymizerStatus.SKIP(inFile, "No pixels");
      }

      // Get the encoding and set the parameters
      DcmDecodeParam fileParam = parser.getDcmDecodeParam();
      String fileEncodingUID = UIDs.ImplicitVRLittleEndian;
      FileMetaInfo fmi = dataset.getFileMetaInfo();
      if (fmi != null) fileEncodingUID = fmi.getTransferSyntaxUID();
      boolean isBigEndian = fileEncodingUID.equals(UIDs.ExplicitVRBigEndian);
      String encodingUID = UIDs.ExplicitVRLittleEndian;
      DcmEncodeParam encoding = (DcmEncodeParam) DcmDecodeParam.valueOf(encodingUID);
      boolean swap = (fileParam.byteOrder != encoding.byteOrder);

      if (encoding.encapsulated) {
        logger.debug("Encapsulated pixel data found");
        close(in);
        return AnonymizerStatus.SKIP(inFile, "Encapsulated pixel data not supported");
      }

      // Get the LUTs
      LUT red =
          new LUT(
              dataset.getInts(Tags.RedPaletteColorLUTDescriptor),
              dataset.getInts(Tags.RedPaletteColorLUTData));
      LUT green =
          new LUT(
              dataset.getInts(Tags.GreenPaletteColorLUTDescriptor),
              dataset.getInts(Tags.GreenPaletteColorLUTData));
      LUT blue =
          new LUT(
              dataset.getInts(Tags.BluePaletteColorLUTDescriptor),
              dataset.getInts(Tags.BluePaletteColorLUTData));

      // Set the PlanarConfiguration to 0
      dataset.putUS(Tags.PlanarConfiguration, 0);

      // Set the PhotometricInterpretation to RGB
      dataset.putCS(Tags.PhotometricInterpretation, "RGB");

      // Set the pixel parameters
      dataset.putUS(Tags.SamplesPerPixel, 3);
      dataset.putUS(Tags.BitsAllocated, 8);
      dataset.putUS(Tags.BitsStored, 8);
      dataset.putUS(Tags.HighBit, 7);

      // Remove the lookup tables and their descriptors
      dataset.remove(Tags.RedPaletteColorLUTDescriptor);
      dataset.remove(Tags.GreenPaletteColorLUTDescriptor);
      dataset.remove(Tags.BluePaletteColorLUTDescriptor);
      dataset.remove(Tags.RedPaletteColorLUTData);
      dataset.remove(Tags.GreenPaletteColorLUTData);
      dataset.remove(Tags.BluePaletteColorLUTData);

      // Save the dataset to a temporary file, and rename at the end.
      File tempDir = outFile.getParentFile();
      tempFile = File.createTempFile("DCMtemp-", ".anon", tempDir);
      out = new BufferedOutputStream(new FileOutputStream(tempFile));

      // Create and write the metainfo for the encoding we are using
      fmi = oFact.newFileMetaInfo(dataset, encodingUID);
      dataset.setFileMetaInfo(fmi);
      fmi.write(out);

      // Write the dataset as far as was parsed
      dataset.writeDataset(out, encoding);

      // Process the pixels
      int nPixels = numberOfFrames * rows * columns;
      int nPixelBytes = nPixels * 3 /*samplesPerPixel*/;
      int pad = nPixelBytes & 1;
      dataset.writeHeader(out, encoding, parser.getReadTag(), VRs.OB, nPixelBytes + pad);

      int pd;
      int b1, b2;
      int bytesPerFrame = rows * columns * 2;
      byte[] frameBytes = new byte[bytesPerFrame];
      for (int frame = 0; frame < numberOfFrames; frame++) {
        if (in.read(frameBytes, 0, frameBytes.length) != bytesPerFrame)
          throw new Exception("End of File");
        for (int p = 0; p < bytesPerFrame; ) {
          b1 = frameBytes[p++];
          b2 = frameBytes[p++];
          if (!swap) {
            pd = ((b2 & 0xff) << 8) | (b1 & 0xff);
          } else {
            pd = ((b1 & 0xff) << 8) | (b2 & 0xff);
          }
          out.write(red.get(pd));
          out.write(green.get(pd));
          out.write(blue.get(pd));
        }
      }
      if (pad != 0) out.write(0);
      logger.debug("Finished writing the pixels");

      // Skip everything after the pixels
      out.flush();
      out.close();
      in.close();
      outFile.delete();
      tempFile.renameTo(outFile);
      return AnonymizerStatus.OK(outFile, "");
    } catch (Exception e) {
      logger.debug("Exception while processing image.", e);

      // Close the input stream if it actually got opened.
      close(in);

      // Close the output stream if it actually got opened,
      // and delete the tempFile in case it is still there.
      try {
        if (out != null) {
          out.close();
          tempFile.delete();
        }
      } catch (Exception ex) {
        logger.warn("Unable to close the output stream.");
      }

      // Quarantine the object
      return AnonymizerStatus.QUARANTINE(inFile, e.getMessage());
    }
  }
/**
 * @author [email protected]
 * @version $Revision: 7709 $ $Date: 2008-10-22 11:41:47 -0300 (qua, 22 out 2008) $
 */
public class XDSIExportDelegate {

  private static final String XDSI_DELEGATE_ATTR_NAME = "xdsiDelegate";

  private static MBeanServer server;
  private static ObjectName keyObjectServiceName;
  private static ObjectName xdsiServiceName;

  private static final DcmObjectFactory dof = DcmObjectFactory.getInstance();

  private static Logger log = Logger.getLogger(XDSIExportDelegate.class.getName());

  public XDSIExportDelegate() {}

  public static final XDSIExportDelegate getInstance(ControllerContext ctx) {
    HttpSession session = ctx.getRequest().getSession();
    XDSIExportDelegate delegate =
        (XDSIExportDelegate) session.getAttribute(XDSI_DELEGATE_ATTR_NAME);
    if (delegate == null) {
      delegate = new XDSIExportDelegate();
      try {
        delegate.init(ctx);
      } catch (Exception e) {
        throw new NullPointerException("Cant initialize XDSIExportDelegate!");
      }
      session.setAttribute(XDSI_DELEGATE_ATTR_NAME, delegate);
    }
    return delegate;
  }

  public void init(ControllerContext ctx) throws Exception {
    if (keyObjectServiceName != null) return;
    server = MBeanServerLocator.locate();
    String s = ctx.getServletConfig().getInitParameter("keyObjectServiceName");
    keyObjectServiceName = new ObjectName(s);
    s = ctx.getServletConfig().getInitParameter("xdsiServiceName");
    xdsiServiceName = new ObjectName(s);
  }

  public boolean exportXDSI(XDSIModel xdsiModel) throws Exception {
    if (xdsiModel.getNumberOfInstances() < 1) {
      throw new IllegalArgumentException("No Instances selected!");
    }
    Collection items = getObserverContextItems(getAuthorPerson(xdsiModel.getUser()));
    Dataset rootInfo = getRootInfo(xdsiModel);
    List contentItems = getContentItems(xdsiModel);
    contentItems.addAll(items);
    try {
      Boolean b;
      if (!xdsiModel.isPdfExport()) {
        Dataset keyObjectDS = getKeyObject(xdsiModel.getInstances(), rootInfo, contentItems);
        b =
            (Boolean)
                server.invoke(
                    xdsiServiceName,
                    "sendSOAP",
                    new Object[] {keyObjectDS, xdsiModel.listMetadataProperties()},
                    new String[] {Dataset.class.getName(), Properties.class.getName()});
      } else {
        String docUID = (String) xdsiModel.getInstances().iterator().next();
        b =
            (Boolean)
                server.invoke(
                    xdsiServiceName,
                    "exportPDF",
                    new Object[] {docUID, xdsiModel.listMetadataProperties()},
                    new String[] {String.class.getName(), Properties.class.getName()});
      }
      return b.booleanValue();
    } catch (Exception e) {
      log.warn("Failed to export Selection:", e);
      throw e;
    }
  }

  public boolean exportPDF(XDSIModel xdsiModel) throws Exception {
    if (xdsiModel.getNumberOfInstances() < 1) {
      throw new IllegalArgumentException("No Instances selected!");
    }
    Collection items = getObserverContextItems(getAuthorPerson(xdsiModel.getUser()));
    Dataset rootInfo = getRootInfo(xdsiModel);
    List contentItems = getContentItems(xdsiModel);
    contentItems.addAll(items);
    String docUID = (String) xdsiModel.getInstances().iterator().next();
    try {
      Boolean b =
          (Boolean)
              server.invoke(
                  xdsiServiceName,
                  "exportPDF",
                  new Object[] {docUID, xdsiModel.listMetadataProperties()},
                  new String[] {String.class.getName(), Properties.class.getName()});
      return b.booleanValue();
    } catch (Exception e) {
      log.warn("Failed to export Selection:", e);
      throw e;
    }
  }

  public boolean createFolder(XDSIModel model) throws Exception {
    try {
      Boolean b =
          (Boolean)
              server.invoke(
                  xdsiServiceName,
                  "createFolder",
                  new Object[] {model.listMetadataProperties()},
                  new String[] {Properties.class.getName()});
      return b.booleanValue();
    } catch (Exception e) {
      log.warn("Failed to create Folder:", e);
      throw e;
    }
  }

  private Collection getObserverContextItems(String personName) {
    Dataset ds = dof.newDataset();
    ds.putCS(Tags.RelationshipType, "HAS OBS CONTEXT");
    ds.putCS(Tags.ValueType, "CODE");
    DcmElement cnSq = ds.putSQ(Tags.ConceptNameCodeSeq);
    Dataset cnDS = cnSq.addNewItem();
    cnDS.putSH(Tags.CodeValue, "121005");
    cnDS.putSH(Tags.CodingSchemeDesignator, "DCM");
    cnDS.putLO(Tags.CodeMeaning, "ObserverType");
    DcmElement ccSq = ds.putSQ(Tags.ConceptCodeSeq);
    Dataset ccDS = ccSq.addNewItem();
    ccDS.putSH(Tags.CodeValue, "121006");
    ccDS.putSH(Tags.CodingSchemeDesignator, "DCM");
    ccDS.putLO(Tags.CodeMeaning, "Person");

    Dataset ds1 = dof.newDataset();
    ds1.putCS(Tags.RelationshipType, "HAS OBS CONTEXT");
    ds1.putCS(Tags.ValueType, "PNAME");
    DcmElement cnSq1 = ds1.putSQ(Tags.ConceptNameCodeSeq);
    Dataset cnDS1 = cnSq1.addNewItem();
    cnDS1.putSH(Tags.CodeValue, "121008");
    cnDS1.putSH(Tags.CodingSchemeDesignator, "DCM");
    cnDS1.putLO(Tags.CodeMeaning, "Person Observer Name");
    ds1.putPN(Tags.PersonName, personName);
    ArrayList col = new ArrayList();
    col.add(ds);
    col.add(ds1);
    return col;
  }

  /**
   * @param user
   * @return
   */
  public String getAuthorPerson(String user) {
    try {
      return (String)
          server.invoke(
              xdsiServiceName,
              "getAuthorPerson",
              new Object[] {user},
              new String[] {String.class.getName()});
    } catch (Exception e) {
      log.warn("Failed to get author person for user " + user + " ! Reason:" + e.getCause());
      return null;
    }
  }

  /**
   * @param xdsiModel
   * @return
   */
  private List getContentItems(XDSIModel xdsiModel) {
    List items = new ArrayList();
    return items;
  }

  /**
   * @param xdsiModel
   * @return
   */
  private Dataset getRootInfo(XDSIModel xdsiModel) {
    Dataset rootInfo = DcmObjectFactory.getInstance().newDataset();
    DcmElement sq = rootInfo.putSQ(Tags.ConceptNameCodeSeq);
    Dataset item = sq.addNewItem();
    CodeItem selectedDocTitle = xdsiModel.selectedDocTitle();
    item.putSH(Tags.CodeValue, selectedDocTitle.getCodeValue());
    item.putSH(Tags.CodingSchemeDesignator, selectedDocTitle.getCodeDesignator());
    item.putLO(Tags.CodeMeaning, selectedDocTitle.getCodeMeaning());
    return rootInfo;
  }

  public Dataset getKeyObject(Collection iuids, Dataset rootInfo, List contentItems) {
    Object o = null;
    try {
      o =
          server.invoke(
              keyObjectServiceName,
              "getKeyObject",
              new Object[] {iuids, rootInfo, contentItems},
              new String[] {
                Collection.class.getName(), Dataset.class.getName(), Collection.class.getName()
              });
    } catch (RuntimeMBeanException x) {
      log.warn("RuntimeException thrown in KeyObject Service:" + x.getCause());
      throw new IllegalArgumentException(x.getCause().getMessage());
    } catch (Exception e) {
      log.warn("Failed to create Key Object:", e);
      throw new IllegalArgumentException(
          "Error: KeyObject Service cant create manifest Key Object! Reason:"
              + e.getClass().getName());
    }
    return (Dataset) o;
  }

  private ContentManager lookupContentManager() throws Exception {
    ContentManagerHome home =
        (ContentManagerHome)
            EJBHomeFactory.getFactory()
                .lookup(ContentManagerHome.class, ContentManagerHome.JNDI_NAME);
    return home.create();
  }

  /** @return */
  public CodeItem[] getConfiguredAuthorRoles() {
    return getCodeItems("listAuthorRoles");
  }

  public CodeItem[] getConfiguredClassCodes() {
    return getCodeItems("listClassCodes");
  }

  public CodeItem[] getConfiguredContentTypeCodes() {
    return getCodeItems("listContentTypeCodes");
  }

  public CodeItem[] getConfiguredHealthCareFacilityTypeCodes() {
    return getCodeItems("listHealthCareFacilityTypeCodes");
  }

  public CodeItem[] getConfiguredEventCodes() {
    return getCodeItems("listEventCodes");
  }

  public CodeItem[] getConfiguredDocTitles() {
    return getCodeItems("listDocTitleCodes");
  }

  public CodeItem[] getConfiguredConfidentialityCodes() {
    return getCodeItems("listConfidentialityCodes");
  }

  private CodeItem[] getCodeItems(String methodName) {
    try {
      List l = (List) server.invoke(xdsiServiceName, methodName, new Object[] {}, new String[] {});
      CodeItem[] items = new CodeItem[l.size()];
      for (int i = 0, len = l.size(); i < len; i++) {
        items[i] = CodeItem.valueofDCM(l.get(i).toString()); // DCM (D)esignator(C)odevalue(M)eaning
      }
      return items;
    } catch (Exception e) {
      log.error("Failed to get list of configured Codes! method:" + methodName, e);
      return null;
    }
  }

  /** @return */
  public Properties joinMetadataProperties(Properties props) {
    try {
      return (Properties)
          server.invoke(
              xdsiServiceName,
              "joinMetadataProperties",
              new Object[] {props},
              new String[] {Properties.class.getName()});
    } catch (Exception e) {
      log.error("Failed to get XDS-I Metadata Properties:", e);
      return null;
    }
  }
}
/**
 * Implementation of C-DIMSE services.
 *
 * <p>
 *
 * <p>Usage:
 *
 * <p>1. Create a new instance of this class.
 *
 * <p>2. Use the aASSOCIATE method to establish an association.
 *
 * <p>3. Use the cFIND method to query the archive.
 *
 * <p>4. Use the cMOVE method to move an object from the archive to a destination server.
 *
 * <p>5. Use the cSTORE to store an object into an archive.
 *
 * <p>6. Use the cECHO to verfy a association.
 *
 * <p>7. If you are ready with the C-DIMSE services use the aRELEASE method to close the
 * association.
 *
 * <p>
 *
 * <p>The query/retrieve levels used by C-FIND and C-MOVE are defined as enummerated constants. Use
 * the method getQueryRetrieveLevel to convert these values to the String representation used in the
 * DICOM element QueryRetrieveLevel (0008,0052).
 *
 * <p>
 *
 * <p>Based on dcm4che 1.4.0 sample: MoveStudy.java revision date 2005-10-05
 *
 * <p>Based on dcm4che 1.4.0 sample: DcmSnd.java revision date 2005-10-05
 *
 * <p>
 *
 * <p>See: PS 3.4 - Annex B STORAGE SERVICE CLASS
 *
 * <p>See: PS 3.4 - Annex C QUERY/RETRIEVE SERVICE CLASS
 *
 * <p>See: PS 3.4 - C.6 SOP CLASS DEFINITIONS
 *
 * <p>See: PS 3.4 - C.6.2 Study Root SOP Class Group
 *
 * <p>
 *
 * <p>Details of how to run the services is given in a configuration property file. A sample may be
 * found at "./resources/CDimseService.cfg".
 *
 * @author Thomas Hacklaender
 * @version 2006-08-28
 */
public class CDimseService {

  static final Logger log = Logger.getLogger("CDimseService");

  /** The DEBUG flag is set, if the logging level of this class is Debug */
  static final boolean DEBUG = log.isDebugEnabled();

  // >>>> Factorys >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  private static final UIDDictionary uidDict =
      DictionaryFactory.getInstance().getDefaultUIDDictionary();
  private static final AssociationFactory aFact = AssociationFactory.getInstance();
  private static final DcmObjectFactory oFact = DcmObjectFactory.getInstance();
  private static final DcmParserFactory pFact = DcmParserFactory.getInstance();
  private static final DcmObjectFactory dof = DcmObjectFactory.getInstance();

  /** Default AE title used for the association if it is not explicit given in the url filed. */
  private static final String DEFAULT_CALLING_AET = "OVIYAM";

  /** Query/Retrieve Level Values for Study = "PATIENT". See PS 3.4 - C.6 SOP CLASS DEFINITIONS */
  public static final int PATIENT_LEVEL = 0;

  /** Query/Retrieve Level Values for Study = "STUDY". See PS 3.4 - C.6. SOP CLASS DEFINITIONS */
  public static final int STUDY_LEVEL = 1;

  /** Query/Retrieve Level Values for Series = "SERIES". See PS 3.4 - C.6 SOP CLASS DEFINITIONS */
  public static final int SERIES_LEVEL = 2;

  /** Query/Retrieve Level Values for Image = "IMAGE". See PS 3.4 - C.6 SOP CLASS DEFINITIONS */
  public static final int IMAGE_LEVEL = 3;

  PresContext pc;

  /**
   * DICOM URL to define a communication partner for an association.
   *
   * <p>PROTOCOL://CALLED[:CALLING]@HOST[:PORT]
   *
   * <p>
   *
   * <p>PROTOCOL Specifies protocol. Possible values:
   *
   * <p>- dicom DICOM default (without TLS)
   *
   * <p>- dicom-tls DICOM on TLS (offer AES and DES encryption)
   *
   * <p>- dicom-tls.aes DICOM on TLS (force AES or DES encryption)
   *
   * <p>- dicom-tls.3des DICOM on TLS (force DES encryption)
   *
   * <p>- dicom-tls.nodes DICOM on TLS (no encryption)
   *
   * <p>CALLED Called AET in association request (max 16 chars)
   *
   * <p>CALLING Calling AET in association request (max 16 chars) [default id final field
   * DEFAULT_CALLING_AET = MYAET]
   *
   * <p>HOST Name or IP address of host, where the server is running
   *
   * <p>PORT TCP port address, on which the server is listing for
   *
   * <p>incoming TCP Transport Connection Indication [default=104]
   */
  private DcmURL url = null;

  /** Message priority. Possible values Command.LOW = 2, Command.MEDIUM = 0, Command.HIGH = 1 */
  private int priority = Command.MEDIUM;

  /**
   * Time-out waiting [in msec] for A-ASSOCIATE-AC acknowlage, 0 is interpreted as an infinite
   * timeout [default=5000].
   */
  private int acTimeout = 5000;

  /**
   * Time-out waiting [in msec] for DIMSE on aASSOCIATE association, 0 is interpreted as an infinite
   * timeout [default=0].
   */
  private int dimseTimeout = 0;

  /** Time delay [in msec] for socket aRELEASE after sending A-ABORT [default=500]. */
  private int soCloseDelay = 500;

  /**
   * Association Request package (A-ASSOCIATE-RQ) is part of the connection service ACSE
   * (Association Control Service Element). In TCP/IP networks connection services are emulated by
   * the "DICOM Upper Layer Service". This presentation srvice encapsulats the data in PDUs
   * (Protocol Data Unit).
   */
  private AAssociateRQ assocRQ = aFact.newAAssociateRQ();

  /** Association object for establishing an active association. */
  private Association assoc = null;

  /** Accepted association */
  private ActiveAssociation aassoc = null;

  /**
   * Activates packing of command PDV (Presentation Data Value) + (first) data PDV into one
   * P-DATA-TF PDU (Protocol Data Unit)
   */
  private boolean packPDVs = false;

  /** TLS context. Set by method initTLS */
  private SSLContextAdapter tls = null;

  /** An array of implemented ciphers for the communication protocol (given in url). */
  private String[] cipherSuites = null;

  /**
   * Specifies Key Attributes used for C-FIND. Wildcards '*','?', '-' and '\' are allowed as
   * element-values (see PS 3.4 - C.2.2.2 Attribute Matching). If an key attribute is defined, but
   * has an empty value, it matches every value at the storage side.
   *
   * <p>Key attributes have a type (PS 3.4 - C.2.2.1 Attribute Types)
   *
   * <p>- U = one Attribute shall be defined as a Unique Key.
   *
   * <p>- R = a set of Attributes shall be defined as Required Keys.
   *
   * <p>- O = a set of Attributes shall be defined as Optional Keys.
   *
   * <p>The complete list of Key Attributes can be found at PS 3.4 - C.6.2 Study Root SOP Class
   * Group.
   *
   * <p>As a result of C-FIND for each matching item in the archive a DIMSE object containing the
   * Key Attributes is send back. In this object the Key Attributes are set corresponding to values
   * found in the archive. Attributes of type "O" may be send back with empty value. Only used by
   * C-FIND.
   */
  private Dataset keys = dof.newDataset();

  /**
   * Application Entity Title (AET) of the destination for the C-MOVE. The AET must be known by the
   * archive together with the host IP adress and the port number. Only used by C-MOVE.
   */
  private String dest;

  // >>>> Constructor >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

  /**
   * Constructor for the StorageSCUServiceClass object. Initializes everything.
   *
   * <p>Details of how to run the server is given in another configuration property file. A sample
   * may be found at "./resources/StorageSCUServiceClass.cfg".
   *
   * @param cfg the configuration properties for this class.
   * @param url the DcmURL of the communication partner.
   * @throws ParseException
   */
  public CDimseService(ConfigProperties cfg, DcmURL url) throws ParseException {
    this.url = url;
    this.priority = Integer.parseInt(cfg.getProperty("prior", "0"));
    this.packPDVs = "true".equalsIgnoreCase(cfg.getProperty("pack-pdvs", "false"));
    initAssocParam(cfg, url);
    // initEchoAssocParam(cfg, url);
    initTLS(cfg);

    // Only used by C-FIND
    initKeys(cfg);

    // Only used by C-MOVE
    this.dest = cfg.getProperty("dest");
  }

  // >>>> Methods >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

  /**
   * Initializes Association related parameters.
   *
   * @param cfg the configuration properties for this class.
   * @param url the DcmURL of the communication partner.
   */
  private final void initAssocParam(ConfigProperties cfg, DcmURL url) {
    String callingAET = null;

    // >>>> Get data for filling the Association object for establishing an
    // >>>> active association from configuration file

    acTimeout = Integer.parseInt(cfg.getProperty("ac-timeout", "5000"));
    dimseTimeout = Integer.parseInt(cfg.getProperty("dimse-timeout", "0"));
    soCloseDelay = Integer.parseInt(cfg.getProperty("so-close-delay", "500"));

    // >>>> Fill the Association Request package (A-ASSOCIATE-RQ)

    // Identifying the communication partner by an AET (Application Entity Title)
    assocRQ.setCalledAET(url.getCalledAET());

    // Identifying ourselves by an AET (Application Entity Title)
    if (url.getCallingAET() != null) {
      callingAET = url.getCallingAET();
    } else {
      callingAET = DEFAULT_CALLING_AET;
    }
    assocRQ.setCallingAET(callingAET);

    // Maximum size of one PDU (Protocol Data Unit)
    assocRQ.setMaxPDULength(Integer.parseInt(cfg.getProperty("max-pdu-len", "16352")));

    // Defines possibilities for asynchron DIMSE communication. Noramly synchron DIMSE communication
    // is used.
    // API doc: AssociationFactory.newAsyncOpsWindow(int maxOpsInvoked, int maxOpsPerfomed)
    // PS 3.7 - Annex D.3.3.3 ASYNCHRONOUS OPERATIONS WINDOW NEGOTIATION
    // maxOpsInvoked: This field shall contain the Maximum-number-operationsinvoked as defined for
    // the Association-requester
    // maxOpsPerfomed: This field shall contain the Maximum-number-operationsperformed as defined
    // for the Association-requester
    assocRQ.setAsyncOpsWindow(
        aFact.newAsyncOpsWindow(Integer.parseInt(cfg.getProperty("max-op-invoked", "0")), 1));

    for (Enumeration it = cfg.keys(); it.hasMoreElements(); ) {
      String key = (String) it.nextElement();

      // Setup available transfer syntaces for storage SOP classes
      // PS 3.4 - Annex B STORAGE SERVICE CLASS
      // PS 3.4 - B.5 STANDARD SOP CLASSES
      if (key.startsWith("pc.")) {
        initPresContext(
            Integer.parseInt(key.substring(3)),
            cfg.tokenize(cfg.getProperty(key), new LinkedList()));
      }
    }
  }

  /**
   * Only used by method initAssocParam: Sets up available transfer syntaces for storage SOP
   * classes.
   *
   * @param pcid is a for the association unique odd number between 1 and 255.
   * @param val a list: First element is the symbolic name of the UID of the SOP to transmit, the
   *     following elements are the supportet transfer syntax for that SOP.
   */
  private final void initPresContext(int pcid, List val) {
    Iterator it = val.iterator();
    String as = UIDs.forName((String) it.next());
    String[] tsUIDs = new String[val.size() - 1];
    for (int i = 0; i < tsUIDs.length; ++i) {
      tsUIDs[i] = UIDs.forName((String) it.next());
    }
    // API doc: AssociationFactory.newPresContext(int pcid, String asuid, String[] tsuid)
    // pcid is a for the association unique odd number between 1 and 255.
    // asuid is the UID of a SOP class
    // TS list of transfer syntaces supported by this class for asuid.
    assocRQ.addPresContext(aFact.newPresContext(pcid, as, tsUIDs));
  }

  /**
   * Initializes TLS (Transport Layer Security, predecessor of SSL, Secure Sockets Layer) connection
   * related parameters. TLS expects RSA (Ron Rivest, Adi Shamir and Len Adleman) encoded keys and
   * certificates.
   *
   * <p>Keys and certificates may be stored in PKCS #12 (Public Key Cryptography Standards) or JKS
   * (Java Keystore) containers.
   *
   * <p>TSL is used to encrypt data during transmission, which is accomplished when the connection
   * between the two partners A (normally the client) and B (normally the server) is established. If
   * A asks B to send TSL encrypted data, both partners exchange some handshake information. In a
   * first step B tries to authentify itself against A (server authentification, optional in TSL but
   * implementet in this way in dcm4che). For that B presents its public key for A to accept or
   * deny. If the authentification is accepted, A tries to authentify itself against B (client
   * authentification, optional in TSL but implementet in this way in dcm4che).If B accepts the
   * authentification A and B agree on a hash (symmetric key, which is independent of the
   * private/public keys used for authentification) for the duration of their conversation, which is
   * used to encrypt the data.
   *
   * <p>To be able to establish a TSL connection B needs a privat/public key pair, which identifies
   * B unambiguously. For that the private key is generated first; than the root-public key is
   * derived from that private key. To bind the root-public key with the identity of B a Certificate
   * Authority (CA) is used: The root-public key is send to CA, which returns a certified-public
   * key, which includes information about the CA as well as the root-public key. Optionally this
   * process can be repeated several times so that the certified-public key contains a chain of
   * certificates of different CA's.
   *
   * <p>The certified-public key of B is presented to A during server authentification. Partner A
   * should accept this key, if it can match the last certificate in the chain with a
   * root-certificat found in its local list of root-certificates of CA's. That means, that A does
   * not check the identity of B, but "trusts" B because it was certified by an authority. The same
   * happens for client authentification. Handling of authentification of the identity of the
   * communication partner is subject of PS 3.15 - Security and System Management Profiles.
   *
   * <p>In the configuration files of this method the certified-public key is stored in the property
   * "tls-key" and the root-certificates of the known CA's in "tls-cacerts".
   *
   * <p>Usually the certified-public keys of A and B are different, but they also may be the same.
   * In this case the root-certificates are also the same.
   *
   * <p>It is possible to establish a TLS connection without using a CA: In this case both partners
   * creates a individual container holding their private and root-public key. These containers
   * could be used for certifying also, because the length of the certifying chain is one and
   * therefore the root-public key is also the last certified-public key. Therefore the root-public
   * key works in this scenario also as the root-certificate of the "certifying authoroty".
   *
   * <p>If no keystores are specified in the configuration properties, the not-certified
   * default-keystore "resources/identityJava.jks" is used for "tls-key" and "tls-cacerts" when
   * establishing the connection.
   *
   * @param cfg the configuration properties for this class.
   * @throws ParseException
   */
  private void initTLS(ConfigProperties cfg) throws ParseException {
    char[] keystorepasswd;
    char[] keypasswd;
    char[] cacertspasswd;
    URL keyURL;
    URL cacertURL;
    String value;

    try {

      // Cipher suites for protokoll:
      // dicom = null
      // dicom-tls = SSL_RSA_WITH_NULL_SHA, TLS_RSA_WITH_AES_128_CBC_SHA,
      // SSL_RSA_WITH_3DES_EDE_CBC_SHA
      // dicom-tls.3des = SSL_RSA_WITH_3DES_EDE_CBC_SHA
      // dicom-tls.aes = TLS_RSA_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA
      // dicom-tls.nodes = SSL_RSA_WITH_NULL_SHA
      cipherSuites = url.getCipherSuites();
      if (cipherSuites == null) {
        return;
      }

      // Get a new TLS context
      tls = SSLContextAdapter.getInstance();

      // >>>> Managing the keystore file containing the privat key and
      // >>>> certified-public key to establish the communication

      // Password of the keystore [default: secret]
      keystorepasswd = cfg.getProperty("tls-keystore-passwd", "secret").toCharArray();

      // Password of the private key [default: secret]
      keypasswd = cfg.getProperty("tls-key-passwd", "secret").toCharArray();

      // URL of the file containing the default-keystore
      keyURL = CDimseService.class.getResource("/resources/identityJava.jks");

      // If availabel, replace URL with the one specified in the configuration file
      if ((value = cfg.getProperty("tls-key")) != null) {
        try {
          // Property specified, try to set to specified value
          keyURL = ConfigProperties.fileRefToURL(CDimseService.class.getResource(""), value);
        } catch (Exception e) {
          log.warn("Wrong value for tls-key: " + value + ". tls-key was set to default value.");
        }
      }

      // log.info("Key URL: " + keyURL.toString());

      // Sets the key attribute of the SSLContextAdapter object
      // API doc: SSLContextAdapter.loadKeyStore(java.net.URL url, char[] password)
      // API doc: SSLContextAdapter.setKey(java.security.KeyStore key, char[] password)
      tls.setKey(tls.loadKeyStore(keyURL, keystorepasswd), keypasswd);

      // >>>> Managing the keystore containing the root-certificates of the Ceritifying Authorities
      // >>>> used for signing the public key

      // Password of the keystore [default: secret]
      cacertspasswd = cfg.getProperty("tls-cacerts-passwd", "secret").toCharArray();

      // URL of the file containing the default-keystore
      cacertURL = CDimseService.class.getResource("/resources/identityJava.jks");

      // If availabel, replace URL with the one specified in the configuration file
      if ((value = cfg.getProperty("tls-cacerts")) != null) {
        try {
          // Property specified, try to set to specified value
          cacertURL = ConfigProperties.fileRefToURL(CDimseService.class.getResource(""), value);
        } catch (Exception e) {
          log.warn(
              "Wrong value for tls-cacerts: " + value + ". tls-cacerts was set to default value.");
        }
      }

      // log.info("Root-certificat of CA URL: " + cacertURL.toString());

      // Sets the trust attribute of the SSLContextAdapter object
      // API doc: SSLContextAdapter.loadKeyStore(java.net.URL url, char[] password)
      // API doc: SSLContextAdapter.setTrust(java.security.KeyStore cacerts)
      tls.setTrust(tls.loadKeyStore(cacertURL, cacertspasswd));

      // Init TLS context adapter
      tls.init();

    } catch (Exception ex) {
      throw new ParseException("Could not initalize TLS configuration.", 0);
    }
  }

  /**
   * Prepares the Dataset representing the search key in C-FIND. As no values are set, the keys
   * match to every content in the archive. The user has to specify concret values to limit the
   * searchSee PS 3.4 - C.6.2.1.2 Study level.
   *
   * <p>As the result for C-FIND these keys are filled with the values found in the archive.
   *
   * @param cfg the configuration properties for this class.
   * @throws ParseException if a given properties for the keys was not found.
   */
  private void initKeys(ConfigProperties cfg) throws ParseException {
    // Remove all keys
    keys = dof.newDataset();

    // Query/Retrieve Level. PS 3.4 - C.6.2 Study Root SOP Class Group
    keys.putCS(Tags.QueryRetrieveLevel, getQueryRetrieveLevel(STUDY_LEVEL));

    // UNIQUE STUDY LEVEL KEY FOR THE STUDY. See PS 3.4 - C.6.2.1.2 Study level
    keys.putUI(Tags.StudyInstanceUID);

    // REQUIRED STUDY LEVEL KEY FOR THE STUDY. See PS 3.4 - C.6.2.1.2 Study level
    keys.putDA(Tags.StudyDate);
    // Not defined: StudyTime
    // Not defined: AccessionNumber
    keys.putPN(Tags.PatientName);
    keys.putLO(Tags.PatientID);
    // Not defined: StudyID

    // OPTIONAL STUDY LEVEL KEY FOR THE STUDY. See PS 3.4 - C.6.2.1.2 Study level
    keys.putUS(Tags.NumberOfStudyRelatedSeries);
    keys.putUS(Tags.NumberOfStudyRelatedInstances);
    // mutch more defined...

    // Add the keys found in the configuration properties
    addQueryKeys(cfg);
  }

  /**
   * Add the query keys found in the configuration properties to the Dataset "keys" used bei the
   * cFIND method.
   *
   * @param cfg the configuration properties for this class.
   * @throws ParseException if a given properties for the keys was not found.
   */
  private void addQueryKeys(ConfigProperties cfg) throws ParseException {
    // Add/replace keys found in the configuration file. Syntax key.<element name> = <element value>
    for (Enumeration it = cfg.keys(); it.hasMoreElements(); ) {
      String key = (String) it.nextElement();
      if (key.startsWith("key.")) {
        try {
          keys.putXX(Tags.forName(key.substring(4)), cfg.getProperty(key));
        } catch (Exception e) {
          throw new ParseException(
              "Illegal entry in configuration filr: " + key + "=" + cfg.getProperty(key), 0);
        }
      }
    }
  }

  /**
   * Sets the Dataset representing the query keys in C-FIND. The user can specify concret values to
   * limit the search.
   *
   * <p>Wildcards '*','?', '-' and '\' are allowed as element-values (see PS 3.4 - C.2.2.2 Attribute
   * Matching). If an key attribute is defined, but has an empty value, it matches every value at
   * the storage side.
   *
   * <p>At a minimum the key "QueryRetrieveLevel" must be set ("STUDY", "SERIES" or "IMAGE"). See PS
   * 3.4 - C.6.2 Study Root SOP Class Group
   *
   * <p>As the result for C-FIND these keys are filled with the values found in the archive.
   *
   * @param ds the Dataset containig the search keys.
   */
  public void setQueryKeys(Dataset ds) {
    keys = ds;
  }

  /**
   * Sets the Dataset representing the query keys in C-FIND. The user can specify concret values to
   * limit the search.
   *
   * <p>Wildcards '*','?', '-' and '\' are allowed as element-values (see PS 3.4 - C.2.2.2 Attribute
   * Matching). If an key attribute is defined, but has an empty value, it matches every value at
   * the storage side.
   *
   * <p>At a minimum the key "QueryRetrieveLevel" must be set ("STUDY", "SERIES" or "IMAGE"). See PS
   * 3.4 - C.6.2 Study Root SOP Class Group
   *
   * <p>As the result for C-FIND these keys are filled with the values found in the archive.
   *
   * @param cfg the configuration properties for this class.
   * @throws ParseException if a given properties for the keys was not found.
   */
  public void setQueryKeys(ConfigProperties cfg) throws ParseException {

    // Remove all keys
    keys = dof.newDataset();

    // Add the keys found in the configuration properties
    addQueryKeys(cfg);
  }

  /**
   * Starts a active association to a communication partner. See PS 3.8 - 7.1 A-ASSOCIATE SERVICE
   *
   * @return true, if association was successful established.
   * @exception ConnectException
   * @exception IOException
   * @exception GeneralSecurityException
   */
  public boolean aASSOCIATE() throws ConnectException, IOException, GeneralSecurityException {

    // No association may be active
    if (assoc != null) {
      throw new ConnectException("Association already established");
    }

    // New Association object for establishing an active association
    assoc = aFact.newRequestor(newSocket(url.getHost(), url.getPort()));

    // >>>> Fill the Association object with relevant data
    assoc.setAcTimeout(acTimeout);
    assoc.setDimseTimeout(dimseTimeout);
    assoc.setSoCloseDelay(soCloseDelay);
    assoc.setPackPDVs(packPDVs);

    // 1. Create an communication channel to the communication partner defined in the Association
    // object
    // 2. Send the A-ASSOCIATE-RQ package
    // 3. Receive the aAssociation acknowlage/reject package from the communication partner as a PDU
    // (Protocol Data Unit)
    PDU assocAC = assoc.connect(assocRQ);

    if (!(assocAC instanceof AAssociateAC)) {
      // Acknowlage is A-ASSOCIATE-RJ
      // Association rejected
      assoc = null;

      // Return aASSOCIATE faild
      return false;
    }
    // Acknowlage is A-ASSOCIATE_AC
    // Association accepted

    // Start the accepted association
    // API doc: AssociationFactory.newActiveAssociation(Association assoc, DcmServiceRegistry
    // services)
    aassoc = aFact.newActiveAssociation(assoc, null);
    aassoc.start();

    // Return successfull opened
    return true;
  }

  /**
   * Creates a stream socket and connects it to the specified port number on the named host.
   *
   * @param host the IP address.
   * @param port the port number.
   * @return the Socket.
   * @exception IOException
   * @exception GeneralSecurityException
   */
  private Socket newSocket(String host, int port) throws IOException, GeneralSecurityException {

    // Test, if a secured connection is needed
    if (cipherSuites != null) {

      // Creates a socket for secured connection.
      // The SSLContextAdapter tls uses the javax.net.ssl package for establishing
      // the connection.
      return tls.getSocketFactory(cipherSuites).createSocket(host, port);
    } else {

      // Creates a standard Java socket for unsecured connection.
      return new Socket(host, port);
    }
  }

  /**
   * Releases the active association. See PS 3.8 - 7.2 A-RELEASE SERVICE
   *
   * @param waitOnRSP if true, method waits until it receives the responds to the release request.
   * @exception InterruptedException Description of the Exception
   * @exception IOException Description of the Exception
   */
  public void aRELEASE(boolean waitOnRSP) throws InterruptedException, IOException {
    if (assoc != null) {
      try {
        aassoc.release(waitOnRSP);
      } finally {
        assoc = null;
        aassoc = null;
      }
    }
  }

  /**
   * Stores a DICOM object in an archive (Storage SCP).
   *
   * <p>See PS 3.4 - Annex B STORAGE SERVICE CLASS.
   *
   * @param ds the Dataset to store.
   * @throws ConnectException
   * @throws ParseException
   * @throws IOException
   * @throws InterruptedException
   * @throws IllegalStateException
   */
  public void cSTORE(Dataset ds)
      throws InterruptedException, IOException, ConnectException, ParseException {
    String sopClassUID;
    String sopInstUID;
    PresContext pc = null;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // SOP Class UID must be given
    if ((sopClassUID = ds.getString(Tags.SOPClassUID)) == null) {

      throw new ParseException("No SOP Class UID in Dataset", 0);
    }

    // SOP Instance UID must be given
    if ((sopInstUID = ds.getString(Tags.SOPInstanceUID)) == null) {
      throw new ParseException("No SOP Instance UID in Dataset", 0);
    }

    // Test, if applicable presentation context was found
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ImplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ExplicitVRBigEndian))
            == null) {

      throw new ConnectException("No applicable presentation context found");
    }

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command cStoreRQ = oFact.newCommand();
    // API doc: Command.initCStoreRQ(int msgID, String sopClassUID, String sopInstUID, int priority)
    cStoreRQ.initCStoreRQ(assoc.nextMsgID(), sopClassUID, sopInstUID, priority);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse storeRq = aFact.newDimse(pc.pcid(), cStoreRQ, ds);

    // PS 3.7 - 9.3.1 C-STORE PROTOCOL, 9.3.1.2 C-STORE-RSP
    // Always returns SUCESS result code.
    // Invoke active association with echo request Dimse
    FutureRSP future = aassoc.invoke(storeRq);
    System.out.println("store is going in this AE");
    // Response to the C-ECHO request.
    // The result cannot be accessed until it has been set.
    Dimse storeRsp = future.get();
    Command rspCmd = storeRsp.getCommand();

    // PS 3.7 - 9.3.5 C-MOVE PROTOCOL, 9.3.5.2 C-ECHO-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // Success
        break;
      default:
        log.error("C-STORE failed: " + Integer.toHexString(status));
        break;
    }
  }

  public Vector cGET(Dataset ds) throws ConnectException, IOException, InterruptedException {
    PresContext pc;
    List dimseList;
    Vector datasetVector;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-MOVE is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelGET,
                        UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelGET,
                        UIDs.ImplicitVRLittleEndian))
            == null) {
      throw new ConnectException(
          "Association does not support presentation context for StudyRootQueryRetrieveInformationModelMOVE SOP.");
    }

    // Get the Study Instance UID of the study to mode
    String suid = ds.getString(Tags.SOPInstanceUID);

    // Prepare info for logging
    String patName = ds.getString(Tags.PatientName);
    String patID = ds.getString(Tags.PatientID);
    String studyDate = ds.getString(Tags.StudyDate);
    String prompt =
        "Study[" + suid + "] from " + studyDate + " for Patient[" + patID + "]: " + patName;

    // log.info("Moving: " + prompt);

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command rqCmd = dof.newCommand();
    // API doc: Command.initCMoveRQ(int msgID, String sopClassUID, int priority, String moveDest)
    rqCmd.initCGetRSP(assoc.nextMsgID(), UIDs.StudyRootQueryRetrieveInformationModelGET, priority);
    Dataset rqDs = dof.newDataset();
    rqDs.putCS(Tags.QueryRetrieveLevel, getQueryRetrieveLevel(STUDY_LEVEL));
    // Only Unique Key allowed in C-MOVE. PS 3.4 -C.2.2.1 Attribute Types
    rqDs.putUI(Tags.SOPInstanceUID, suid);
    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse moveRq = aFact.newDimse(pc.pcid(), rqCmd, rqDs);

    // Invoke active association with move request Dimse
    FutureRSP future = aassoc.invoke(moveRq);
    // Response to the C-MOVE request.
    // The result cannot be accessed until it has been set.
    Dimse moveRsp = future.get();
    Command rspCmd = moveRsp.getCommand();

    if (DEBUG) {
      StringWriter w = new StringWriter();
      w.write("C-FIND RQ Identifier:\n");
      keys.dumpDataset(w, null);
      log.debug(w.toString());
    }

    // Invoke active association with find request Dimse

    // Response to the C-FIND request.
    // The result cannot be accessed until it has been set.

    // Get the list of found objects
    dimseList = future.listPending();

    // >>>> Extract Dataset from Dimse

    datasetVector = new Vector();

    // If no List of DIMSE objects was generated or it is empty return an empty Vector
    if (dimseList == null || dimseList.isEmpty()) {
      return datasetVector;
    }

    // Process all elements
    for (int i = 0; i < dimseList.size(); i++) {
      datasetVector.addElement(((Dimse) dimseList.get(i)).getDataset());
      if (((Dimse) dimseList.get(i)).getDataset() == null)
        System.out.println("              Dataset created succesffullyu          ");
    }

    // PS 3.7 - 9.3.4 C-MOVE PROTOCOL, 9.3.4.2 C-MOVE-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // log.info("Moved: " + prompt);
        break;
      case 0xB000:
        log.error("One or more failures during move of " + prompt);
        break;
      default:
        log.error("Failed to move " + prompt + "\n\terror tstatus: " + Integer.toHexString(status));
        break;
    }
    System.out.println("The move sise is : " + datasetVector.size());
    return datasetVector;
  }

  /**
   * Queries the archive for DICOM objects matching Attribute Keys defined in the loacal field
   * "keys". This field is set by the constructor out of the configuration parameters or by the
   * methods setQueryKeys(Configuration) and setQueryKeys(Dataset). See PS 3.4 - Annex C
   * QUERY/RETRIEVE SERVICE CLASS.
   *
   * <p>The method returns, when the result is received from the communication partner.
   *
   * @return the result of the cFIND as a Vector of Dataset objects each specifying one matching
   *     DICOM object. If no matching objects are found an empty Vector is returned.
   * @throws ConnectException
   * @throws IOException
   */
  public Vector cFIND() throws ConnectException, IOException, InterruptedException {

    List dimseList;
    Vector datasetVector;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-FIND is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    //        UIDs.StudyRootQueryRetrieveInformationModelGET
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelFIND,
                        UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelFIND,
                        UIDs.ImplicitVRLittleEndian))
            == null) {
      throw new ConnectException(
          "Association does not support presentation context for StudyRootQueryRetrieveInformationModelFIND SOP.");
    }

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command rqCmd = dof.newCommand();
    // API doc: Command.initCFindRQ(int msgID, String sopClassUID, int priority)
    rqCmd.initCFindRQ(assoc.nextMsgID(), UIDs.StudyRootQueryRetrieveInformationModelFIND, priority);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse findRq = aFact.newDimse(pc.pcid(), rqCmd, keys);

    if (DEBUG) {
      StringWriter w = new StringWriter();
      w.write("C-FIND RQ Identifier:\n");
      keys.dumpDataset(w, null);
      log.debug(w.toString());
    }

    // Invoke active association with find request Dimse
    FutureRSP future = aassoc.invoke(findRq);
    // Response to the C-FIND request.
    // The result cannot be accessed until it has been set.
    Dimse findRsp = future.get();

    // Get the list of found objects
    dimseList = future.listPending();

    // >>>> Extract Dataset from Dimse

    datasetVector = new Vector();

    // If no List of DIMSE objects was generated or it is empty return an empty Vector
    if (dimseList == null || dimseList.isEmpty()) {
      return datasetVector;
    }

    // Process all elements
    for (int i = 0; i < dimseList.size(); i++) {
      datasetVector.addElement(((Dimse) dimseList.get(i)).getDataset());
    }

    return datasetVector;
  }

  /**
   * Ask archive to move one DICOM object to the destination (C-MOVE). See PS 3.4 - Annex C
   * QUERY/RETRIEVE SERVICE CLASS.
   *
   * <p>Use the Study Root Query/Retrieve Information Model to communicate with the archive. See PS
   * 3.4 - C.6.2 Study Root SOP Class Group.
   *
   * <p>PS 3.4 - C.4.2.1.4.1 Request Identifier Structure (for C-MOVE): An Identifier in a C-MOVE
   * request shall contain:
   *
   * <p>- the Query/Retrieve Level (0008,0052) which defines the level of the retrieval
   *
   * <p>- Unique Key Attributes which may include Patient ID, Study Instance UIDs, Series Instance
   * UIDs, and the SOP Instance UIDs
   *
   * <p>PS 3.4 - C.4.2.2.1 Baseline Behavior of SCU (of C-MOVE):
   *
   * <p>The SCU shall supply a single value in the Unique Key Attribute for each level above the
   * Query/Retrieve level. For the level of retrieve, the SCU shall supply one unique key if the
   * level of retrieve is above the STUDY level and shall supply one UID, or a list of UIDs if a
   * retrieval of several items is desired and the retrieve level is STUDY, SERIES or IMAGE.
   *
   * @param ds the DICOM object represented as a Dataset.
   * @return a result-code: 0x0000 = SUCCESS sub-operations complete no failures, 0xB000 = WARNING
   *     sub-operations complete one or more failures, other = errors defined in PS 3.4 - C.4.2.1.5
   *     Status
   * @throws ConnectException
   * @throws InterruptedException
   * @throws IOException
   */
  public Vector cMOVE(Dataset ds) throws ConnectException, InterruptedException, IOException {
    PresContext pc;
    List dimseList;
    Vector datasetVector;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-MOVE is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelMOVE,
                        UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelMOVE,
                        UIDs.ImplicitVRLittleEndian))
            == null) {
      throw new ConnectException(
          "Association does not support presentation context for StudyRootQueryRetrieveInformationModelMOVE SOP.");
    }

    // Get the Study Instance UID of the study to mode
    String suid = ds.getString(Tags.StudyInstanceUID);

    // Prepare info for logging
    String patName = ds.getString(Tags.PatientName);
    String patID = ds.getString(Tags.PatientID);
    String studyDate = ds.getString(Tags.StudyDate);
    String prompt =
        "Study[" + suid + "] from " + studyDate + " for Patient[" + patID + "]: " + patName;

    // log.info("Moving: " + prompt);

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command rqCmd = dof.newCommand();
    // API doc: Command.initCMoveRQ(int msgID, String sopClassUID, int priority, String moveDest)
    rqCmd.initCMoveRQ(
        assoc.nextMsgID(), UIDs.StudyRootQueryRetrieveInformationModelMOVE, priority, dest);
    Dataset rqDs = dof.newDataset();
    rqDs.putCS(Tags.QueryRetrieveLevel, getQueryRetrieveLevel(STUDY_LEVEL));
    // Only Unique Key allowed in C-MOVE. PS 3.4 -C.2.2.1 Attribute Types
    rqDs.putUI(Tags.StudyInstanceUID, suid);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse moveRq = aFact.newDimse(pc.pcid(), rqCmd, rqDs);

    // Invoke active association with move request Dimse
    FutureRSP future = aassoc.invoke(moveRq);
    // Response to the C-MOVE request.
    // The result cannot be accessed until it has been set.
    Dimse moveRsp = future.get();
    Command rspCmd = moveRsp.getCommand();
    Dataset dds = moveRsp.getDataset();

    if (DEBUG) {
      StringWriter w = new StringWriter();
      w.write("C-FIND RQ Identifier:\n");
      keys.dumpDataset(w, null);
      log.debug(w.toString());
    }

    // Invoke active association with find request Dimse

    // Response to the C-FIND request.
    // The result cannot be accessed until it has been set.

    // Get the list of found objects
    Dimse dms = future.get();

    // >>>> Extract Dataset from Dimse

    datasetVector = new Vector();

    // If no List of DIMSE objects was generated or it is empty return an empty Vector
    //  if (dimseList == null || dimseList.isEmpty()) {
    //   return datasetVector;
    //  }

    // Process all elements
    //  for (int i = 0; i < dimseList.size(); i++) {
    //   datasetVector.addElement(((Dimse) dimseList.get(i)).getDataset());
    //  if(((Dimse) dimseList.get(i)).getDataset()==null)
    //  System.out.println("              Dataset created succesffullyu          ");
    //  }

    // PS 3.7 - 9.3.4 C-MOVE PROTOCOL, 9.3.4.2 C-MOVE-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // log.info("Moved: " + prompt);
        break;
      case 0xB000:
        log.error("One or more failures during move of " + prompt);
        break;
      default:
        log.error("Failed to move " + prompt + "\n\terror tstatus: " + Integer.toHexString(status));
        break;
    }
    //  System.out.println("The move sise is : "+datasetVector.size());
    return datasetVector;
  }

  /**
   * Implements the ECHO service. The C-ECHO service is invoked by a DIMSE-service-user to verify
   * end-to-end communications with a peer DIMSE-service-user. See PS 3.7 - 9.1.5 C-ECHO SERVICE
   *
   * @exception ConnectException
   * @exception InterruptedException
   * @exception IOException
   */
  public long cECHO() throws ConnectException, InterruptedException, IOException {
    PresContext pc;
    long t1 = System.currentTimeMillis();

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-ECHO is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    if ((pc =
            aassoc
                .getAssociation()
                .getAcceptedPresContext(UIDs.Verification, UIDs.ImplicitVRLittleEndian))
        == null) {
      throw new ConnectException(
          "Association does not support presentation context: Verification SOP/ImplicitVRLittleEndian.");
    }

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command cEchoRQ = oFact.newCommand();
    // API doc: Command.initCEchoRQ(int msgID)
    cEchoRQ.initCEchoRQ(1);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse echoRq = aFact.newDimse(pc.pcid(), cEchoRQ);

    // PS 3.7 - 9.3.5 C-ECHO PROTOCOL, 9.3.5.2 C-ECHO-RSP
    // Always returns SUCESS result code.
    // Invoke active association with echo request Dimse
    FutureRSP future = aassoc.invoke(echoRq);

    // Response to the C-ECHO request.
    // The result cannot be accessed until it has been set.
    Dimse echoRsp = future.get();
    Command rspCmd = echoRsp.getCommand();

    // PS 3.7 - 9.3.5 C-MOVE PROTOCOL, 9.3.5.2 C-ECHO-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // Success
        break;
      default:
        log.error("C-ECHO failed: " + Integer.toHexString(status));
        break;
    }

    return System.currentTimeMillis() - t1;
  }

  /**
   * Gets the String value used for DICOM element QueryRetrieveLevel (0008,0052).
   *
   * <p>See PS 3.4 - C.6 SOP CLASS DEFINITIONS
   *
   * @param queryRetrieveLevel query/retrieve level as a enumerated value.
   * @return the String value assiciated to enumerated query/retrieve level.
   */
  public static String getQueryRetrieveLevel(int queryRetrieveLevel) {
    switch (queryRetrieveLevel) {
      case PATIENT_LEVEL:
        return "PATIENT";

      case STUDY_LEVEL:
        return "STUDY";

      case SERIES_LEVEL:
        return "SERIES";

      case IMAGE_LEVEL:
        return "IMAGE";

      default:
        return "";
    }
  }
}
  /**
   * Stores a DICOM object in an archive (Storage SCP).
   *
   * <p>See PS 3.4 - Annex B STORAGE SERVICE CLASS.
   *
   * @param ds the Dataset to store.
   * @throws ConnectException
   * @throws ParseException
   * @throws IOException
   * @throws InterruptedException
   * @throws IllegalStateException
   */
  public void cSTORE(Dataset ds)
      throws InterruptedException, IOException, ConnectException, ParseException {
    String sopClassUID;
    String sopInstUID;
    PresContext pc = null;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // SOP Class UID must be given
    if ((sopClassUID = ds.getString(Tags.SOPClassUID)) == null) {

      throw new ParseException("No SOP Class UID in Dataset", 0);
    }

    // SOP Instance UID must be given
    if ((sopInstUID = ds.getString(Tags.SOPInstanceUID)) == null) {
      throw new ParseException("No SOP Instance UID in Dataset", 0);
    }

    // Test, if applicable presentation context was found
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ImplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(sopClassUID, UIDs.ExplicitVRBigEndian))
            == null) {

      throw new ConnectException("No applicable presentation context found");
    }

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command cStoreRQ = oFact.newCommand();
    // API doc: Command.initCStoreRQ(int msgID, String sopClassUID, String sopInstUID, int priority)
    cStoreRQ.initCStoreRQ(assoc.nextMsgID(), sopClassUID, sopInstUID, priority);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse storeRq = aFact.newDimse(pc.pcid(), cStoreRQ, ds);

    // PS 3.7 - 9.3.1 C-STORE PROTOCOL, 9.3.1.2 C-STORE-RSP
    // Always returns SUCESS result code.
    // Invoke active association with echo request Dimse
    FutureRSP future = aassoc.invoke(storeRq);
    System.out.println("store is going in this AE");
    // Response to the C-ECHO request.
    // The result cannot be accessed until it has been set.
    Dimse storeRsp = future.get();
    Command rspCmd = storeRsp.getCommand();

    // PS 3.7 - 9.3.5 C-MOVE PROTOCOL, 9.3.5.2 C-ECHO-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // Success
        break;
      default:
        log.error("C-STORE failed: " + Integer.toHexString(status));
        break;
    }
  }
/**
 * @author franz.willer
 *     <p>TODO To change the template for this generated type comment go to Window - Preferences -
 *     Java - Code Style - Code Templates
 */
public class ECGSupport {

  public static final String CONTENT_TYPE_SVGXML = "image/svg+xml";
  private static Logger log = Logger.getLogger(ECGSupport.class.getName());

  private static final DcmObjectFactory factory = DcmObjectFactory.getInstance();
  private RIDSupport ridSupport;

  private RIDStorageDelegate storage = RIDStorageDelegate.getInstance();

  public ECGSupport(RIDSupport ridSupport) {
    this.ridSupport = ridSupport;
  }

  /**
   * @param reqObj
   * @param ds
   * @return
   */
  public RIDResponseObject getECGDocument(RIDRequestObject reqObj, Dataset ds) {
    String contentType = reqObj.getParam("preferredContentType");
    if (contentType.equals(CONTENT_TYPE_SVGXML) || contentType.equals("image/svg")) {
      contentType = CONTENT_TYPE_SVGXML;
      if (ridSupport.checkContentType(reqObj, new String[] {CONTENT_TYPE_SVGXML}) == null) {
        return new RIDStreamResponseObjectImpl(
            null,
            RIDSupport.CONTENT_TYPE_HTML,
            HttpServletResponse.SC_BAD_REQUEST,
            "Display actor doesnt accept preferred content type!");
      }
    } else if (contentType.equals(RIDSupport.CONTENT_TYPE_PDF)) {
      if (ridSupport.checkContentType(reqObj, new String[] {RIDSupport.CONTENT_TYPE_PDF}) == null) {
        return new RIDStreamResponseObjectImpl(
            null,
            RIDSupport.CONTENT_TYPE_HTML,
            HttpServletResponse.SC_BAD_REQUEST,
            "Display actor doesnt accept preferred content type!");
      }
    } else {
      return new RIDStreamResponseObjectImpl(
          null,
          RIDSupport.CONTENT_TYPE_HTML,
          HttpServletResponse.SC_NOT_ACCEPTABLE,
          "preferredContentType '"
              + contentType
              + "' is not supported! Only 'application/pdf' and 'image/svg+xml' are supported !");
    }
    String docUID = reqObj.getParam("documentUID");
    log.info("get ECG document! docUID:" + docUID);
    BaseDocument doc = storage.getDocument(docUID, contentType);
    try {
      if (doc != null) {
        return new RIDStreamResponseObjectImpl(
            doc.getInputStream(), contentType, HttpServletResponse.SC_OK, null);
      }
    } catch (Exception x) {
      log.error("Getting InputStream for RID Response of document " + docUID + " failed!", x);
    }
    try {
      Dataset fullDS = getDataset(ds);
      doc = storage.createDocument(docUID, contentType);
      if (fullDS == null) {
        return new RIDStreamResponseObjectImpl(
            null,
            RIDSupport.CONTENT_TYPE_HTML,
            HttpServletResponse.SC_NOT_FOUND,
            "Requested document not found::" + ds.getString(Tags.SOPClassUID));
      } else {
        if (contentType.equals(RIDSupport.CONTENT_TYPE_PDF)) {
          return handlePDF(fullDS, doc);
        } else {
          return handleSVG(fullDS, doc);
        }
      }
    } catch (Exception e) {
      return new RIDStreamResponseObjectImpl(
          null,
          RIDSupport.CONTENT_TYPE_HTML,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "Internal server error:" + e.getMessage());
    }
  }

  /**
   * @param string
   * @return
   * @throws NeedRedirectionException
   * @throws IOException
   */
  private Dataset getDataset(Dataset ds) throws IOException {
    String iuid = ds.getString(Tags.SOPInstanceUID);
    File file = ridSupport.getDICOMFile(iuid);
    if (log.isDebugEnabled())
      log.debug("DCM file for " + ds.getString(Tags.SOPInstanceUID) + ":" + file);
    if (file == null) return null;
    Dataset dsFile;
    if (!ridSupport.isUseOrigFile()) {
      FileDataSource dsrc = null;
      try {
        dsrc =
            (FileDataSource)
                ridSupport
                    .getMBeanServer()
                    .invoke(
                        ridSupport.getQueryRetrieveScpName(),
                        "getDatasourceOfInstance",
                        new Object[] {iuid},
                        new String[] {String.class.getName()});
      } catch (Exception e) {
        log.error("Failed to get updated DICOM file", e);
      }
      file = new File(file + ".dcm");
      file.deleteOnExit();
      OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
      dsrc.isWriteFile();
      dsrc.writeTo(os, null);
      os.close();
      dsFile = loadDataset(file);
      file.delete();
    } else {
      dsFile = loadDataset(file);
    }
    return dsFile;
  }

  private Dataset loadDataset(File file) throws IOException {
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
    Dataset ds = factory.newDataset();
    try {
      ds.readFile(bis, null, -1);
    } finally {
      try {
        bis.close();
      } catch (IOException ignore) {
      }
    }
    if (log.isDebugEnabled()) log.debug("Dataset for file " + file + " :" + ds);
    return ds;
  }

  /**
   * @param ds
   * @param outFile
   * @return
   * @throws IOException
   */
  private RIDResponseObject handleSVG(Dataset ds, BaseDocument doc) throws IOException {
    OutputStream out = doc.getOutputStream();
    try {
      DcmElement elem = ds.get(Tags.WaveformSeq);
      WaveformGroup wfgrp =
          new WaveformGroup(
              ds.getString(Tags.SOPClassUID),
              elem,
              0,
              ridSupport.getWaveformCorrection()); // TODO all groups
      if (log.isDebugEnabled()) log.debug(wfgrp);
      WaveformInfo wfInfo = new WaveformInfo(ds);

      SVGCreator svgCreator = new SVGCreator(wfgrp, wfInfo, new Float(27.6f), new Float(20.3f));
      svgCreator.toXML(out);
      out.close();
      return new RIDStreamResponseObjectImpl(
          doc.getInputStream(), CONTENT_TYPE_SVGXML, HttpServletResponse.SC_OK, null);
    } catch (Throwable t) {
      if (out != null)
        try {
          out.close();
        } catch (IOException e) {
        }
      log.error("Cant create SVG for Waveform!", t);
      log.error("Waveform Dataset:");
      log.error(ds);
      return new RIDStreamResponseObjectImpl(
          null,
          RIDSupport.CONTENT_TYPE_HTML,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "Error while creating waveform SVG! Reason:" + t.getMessage());
    }
  }

  private RIDResponseObject handlePDF(Dataset ds, BaseDocument doc) throws IOException {
    OutputStream out = doc.getOutputStream();
    OutputStream tmpOut = null;
    File tmpFile = null;
    try {
      tmpFile = File.createTempFile("fop_", null);
      tmpFile.deleteOnExit();
      tmpOut = new FileOutputStream(tmpFile);

      DcmElement elem = ds.get(Tags.WaveformSeq);
      WaveformGroup[] wfgrps = getWaveformGroups(elem, ds.getString(Tags.SOPClassUID));
      WaveformInfo wfInfo = new WaveformInfo(ds);

      FOPCreator fopCreator = new FOPCreator(wfgrps, wfInfo, new Float(28.6f), new Float(20.3f));
      fopCreator.toXML(tmpOut);
      tmpOut.close();
      Fop fop = ridSupport.newFop(MimeConstants.MIME_PDF, out);
      SAXTransformerFactory tf = (SAXTransformerFactory) TransformerFactory.newInstance();
      Transformer t = tf.newTransformer();
      t.transform(
          new StreamSource(new FileInputStream(tmpFile)), new SAXResult(fop.getDefaultHandler()));
      out.close();
      tmpFile.delete();
      InputStream in = doc.getInputStream();
      return new RIDStreamResponseObjectImpl(
          in, in.available(), RIDSupport.CONTENT_TYPE_PDF, HttpServletResponse.SC_OK, null);
    } catch (Throwable t) {
      try {
        if (out != null) out.close();
        if (tmpOut != null) tmpOut.close();
      } catch (IOException e) {
      }
      if (tmpFile.exists()) tmpFile.delete();
      log.error("Cant create PDF for Waveform!", t);
      log.error("Waveform Dataset:");
      log.error(ds);
      return new RIDStreamResponseObjectImpl(
          null,
          RIDSupport.CONTENT_TYPE_HTML,
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "Error while creating waveform PDF! Reason:" + t.getMessage());
    }
  }

  /**
   * @param cuid
   * @param elem
   * @return
   */
  private WaveformGroup[] getWaveformGroups(DcmElement elem, String cuid) {
    float corr = ridSupport.getWaveformCorrection();
    int nrOfWFGroups = elem.countItems();
    if (nrOfWFGroups == 1)
      return new WaveformGroup[] {new WaveformGroup(cuid, elem, 0, corr)}; // dont
    // check
    // the
    // only
    // one

    ArrayList l = new ArrayList(nrOfWFGroups);
    for (int i = 0; i < nrOfWFGroups; i++) {
      try {
        l.add(new WaveformGroup(cuid, elem, i, corr));
      } catch (Exception x) {
        log.warn("Item " + i + " in Waveform Sequence is not valid! Ignored!!");
      }
    }
    return (WaveformGroup[]) l.toArray(new WaveformGroup[l.size()]);
  }
}
  public Vector cGET(Dataset ds) throws ConnectException, IOException, InterruptedException {
    PresContext pc;
    List dimseList;
    Vector datasetVector;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-MOVE is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelGET,
                        UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelGET,
                        UIDs.ImplicitVRLittleEndian))
            == null) {
      throw new ConnectException(
          "Association does not support presentation context for StudyRootQueryRetrieveInformationModelMOVE SOP.");
    }

    // Get the Study Instance UID of the study to mode
    String suid = ds.getString(Tags.SOPInstanceUID);

    // Prepare info for logging
    String patName = ds.getString(Tags.PatientName);
    String patID = ds.getString(Tags.PatientID);
    String studyDate = ds.getString(Tags.StudyDate);
    String prompt =
        "Study[" + suid + "] from " + studyDate + " for Patient[" + patID + "]: " + patName;

    // log.info("Moving: " + prompt);

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command rqCmd = dof.newCommand();
    // API doc: Command.initCMoveRQ(int msgID, String sopClassUID, int priority, String moveDest)
    rqCmd.initCGetRSP(assoc.nextMsgID(), UIDs.StudyRootQueryRetrieveInformationModelGET, priority);
    Dataset rqDs = dof.newDataset();
    rqDs.putCS(Tags.QueryRetrieveLevel, getQueryRetrieveLevel(STUDY_LEVEL));
    // Only Unique Key allowed in C-MOVE. PS 3.4 -C.2.2.1 Attribute Types
    rqDs.putUI(Tags.SOPInstanceUID, suid);
    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse moveRq = aFact.newDimse(pc.pcid(), rqCmd, rqDs);

    // Invoke active association with move request Dimse
    FutureRSP future = aassoc.invoke(moveRq);
    // Response to the C-MOVE request.
    // The result cannot be accessed until it has been set.
    Dimse moveRsp = future.get();
    Command rspCmd = moveRsp.getCommand();

    if (DEBUG) {
      StringWriter w = new StringWriter();
      w.write("C-FIND RQ Identifier:\n");
      keys.dumpDataset(w, null);
      log.debug(w.toString());
    }

    // Invoke active association with find request Dimse

    // Response to the C-FIND request.
    // The result cannot be accessed until it has been set.

    // Get the list of found objects
    dimseList = future.listPending();

    // >>>> Extract Dataset from Dimse

    datasetVector = new Vector();

    // If no List of DIMSE objects was generated or it is empty return an empty Vector
    if (dimseList == null || dimseList.isEmpty()) {
      return datasetVector;
    }

    // Process all elements
    for (int i = 0; i < dimseList.size(); i++) {
      datasetVector.addElement(((Dimse) dimseList.get(i)).getDataset());
      if (((Dimse) dimseList.get(i)).getDataset() == null)
        System.out.println("              Dataset created succesffullyu          ");
    }

    // PS 3.7 - 9.3.4 C-MOVE PROTOCOL, 9.3.4.2 C-MOVE-RSP
    int status = rspCmd.getStatus();
    switch (status) {
      case 0x0000:
        // log.info("Moved: " + prompt);
        break;
      case 0xB000:
        log.error("One or more failures during move of " + prompt);
        break;
      default:
        log.error("Failed to move " + prompt + "\n\terror tstatus: " + Integer.toHexString(status));
        break;
    }
    System.out.println("The move sise is : " + datasetVector.size());
    return datasetVector;
  }
  /**
   * Queries the archive for DICOM objects matching Attribute Keys defined in the loacal field
   * "keys". This field is set by the constructor out of the configuration parameters or by the
   * methods setQueryKeys(Configuration) and setQueryKeys(Dataset). See PS 3.4 - Annex C
   * QUERY/RETRIEVE SERVICE CLASS.
   *
   * <p>The method returns, when the result is received from the communication partner.
   *
   * @return the result of the cFIND as a Vector of Dataset objects each specifying one matching
   *     DICOM object. If no matching objects are found an empty Vector is returned.
   * @throws ConnectException
   * @throws IOException
   */
  public Vector cFIND() throws ConnectException, IOException, InterruptedException {

    List dimseList;
    Vector datasetVector;

    // An association must be active
    if (aassoc == null) {
      throw new ConnectException("No Association established");
    }

    // Test, if Presentation Context for C-FIND is supported
    // API doc: Association.getAcceptedPresContext(String asuid, String tsuid)
    //        UIDs.StudyRootQueryRetrieveInformationModelGET
    if ((pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelFIND,
                        UIDs.ExplicitVRLittleEndian))
            == null
        && (pc =
                aassoc
                    .getAssociation()
                    .getAcceptedPresContext(
                        UIDs.StudyRootQueryRetrieveInformationModelFIND,
                        UIDs.ImplicitVRLittleEndian))
            == null) {
      throw new ConnectException(
          "Association does not support presentation context for StudyRootQueryRetrieveInformationModelFIND SOP.");
    }

    // New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command Set Structure
    Command rqCmd = dof.newCommand();
    // API doc: Command.initCFindRQ(int msgID, String sopClassUID, int priority)
    rqCmd.initCFindRQ(assoc.nextMsgID(), UIDs.StudyRootQueryRetrieveInformationModelFIND, priority);

    // API doc: AssociationFactorynewDimse(int pcid, Command cmd, Dataset ds)
    // DIMSE (DICOM Message Service Element) ist ein Nachrichtendienst in DICOM
    Dimse findRq = aFact.newDimse(pc.pcid(), rqCmd, keys);

    if (DEBUG) {
      StringWriter w = new StringWriter();
      w.write("C-FIND RQ Identifier:\n");
      keys.dumpDataset(w, null);
      log.debug(w.toString());
    }

    // Invoke active association with find request Dimse
    FutureRSP future = aassoc.invoke(findRq);
    // Response to the C-FIND request.
    // The result cannot be accessed until it has been set.
    Dimse findRsp = future.get();

    // Get the list of found objects
    dimseList = future.listPending();

    // >>>> Extract Dataset from Dimse

    datasetVector = new Vector();

    // If no List of DIMSE objects was generated or it is empty return an empty Vector
    if (dimseList == null || dimseList.isEmpty()) {
      return datasetVector;
    }

    // Process all elements
    for (int i = 0; i < dimseList.size(); i++) {
      datasetVector.addElement(((Dimse) dimseList.get(i)).getDataset());
    }

    return datasetVector;
  }
Exemple #22
0
 public void handleNotification(Notification notif, Object handback) {
   String spsuid = (String) notif.getUserData();
   Dataset pps = DcmObjectFactory.getInstance().newDataset();
   try {
     Dataset sps;
     GPWLManager gpwlmgr = getGPWLManager();
     sps = gpwlmgr.getWorklistItem(spsuid);
     String ppsiuid = spsuid + ppsuidSuffix;
     String status = sps.getString(Tags.GPSPSStatus);
     pps.putCS(Tags.GPPPSStatus, status);
     pps.putUI(Tags.SOPInstanceUID, ppsiuid);
     Date now = new Date();
     if ("IN PROGRESS".equals(status)) {
       try {
         getGPPPSManager().getGPPPS(ppsiuid);
         return; // avoid duplicate N_CREATE
       } catch (Exception e) {
       }
       pps.putSH(Tags.PPSID, "PPS" + ppsiuid.hashCode());
       pps.putDA(Tags.PPSStartDate, now);
       pps.putTM(Tags.PPSStartTime, now);
       pps.putDA(Tags.PPSEndDate);
       pps.putTM(Tags.PPSEndTime);
       for (int i = 0; i < N_CREATE_TYPE2_ATTRS.length; i++) {
         pps.putXX(N_CREATE_TYPE2_ATTRS[i]);
       }
       pps.putAll(sps.subSet(N_CREATE_SPS_ATTRS));
       copyCode(
           copyWorkitemCode,
           sps.getItem(Tags.ScheduledWorkitemCodeSeq),
           pps.putSQ(Tags.PerformedWorkitemCodeSeq));
       copyCode(
           copyStationNameCode,
           sps.getItem(Tags.ScheduledStationNameCodeSeq),
           pps.putSQ(Tags.PerformedStationNameCodeSeq));
       copyCode(
           copyStationClassCode,
           sps.getItem(Tags.ScheduledStationClassCodeSeq),
           pps.putSQ(Tags.PerformedStationClassCodeSeq));
       copyCode(
           copyStationGeographicLocationCode,
           sps.getItem(Tags.ScheduledStationGeographicLocationCodeSeq),
           pps.putSQ(Tags.PerformedStationGeographicLocationCodeSeq));
       copyCode(
           copyProcessingApplicationsCode,
           sps.getItem(Tags.ScheduledProcessingApplicationsCodeSeq),
           pps.putSQ(Tags.PerformedProcessingApplicationsCodeSeq));
     } else if ("COMPLETED".equals(status) || "DISCONTINUED".equals(status)) {
       pps.putDA(Tags.PPSEndDate, now);
       pps.putTM(Tags.PPSEndTime, now);
       pps.putAll(gpwlmgr.getOutputInformation(spsuid));
     } else {
       return;
     }
   } catch (Exception e) {
     log.error("Failed to access GP-SPS[" + spsuid + "]", e);
     return;
   }
   for (int i = 0; i < destAETs.length; i++) {
     PPSOrder order = new PPSOrder(pps, destAETs[i]);
     try {
       log.info("Scheduling " + order);
       jmsDelegate.queue(queueName, order, Message.DEFAULT_PRIORITY, 0L);
     } catch (Exception e) {
       log.error("Failed to schedule " + order, e);
     }
   }
 }
Exemple #23
0
 public static int maxDiffPixelData(File f1, File f2) throws IOException {
   int maxDiff = 0;
   InputStream in1 = new BufferedInputStream(new FileInputStream(f1));
   try {
     InputStream in2 = new BufferedInputStream(new FileInputStream(f2));
     try {
       DcmObjectFactory df = DcmObjectFactory.getInstance();
       DcmParserFactory pf = DcmParserFactory.getInstance();
       Dataset attrs1 = df.newDataset();
       Dataset attrs2 = df.newDataset();
       DcmParser p1 = pf.newDcmParser(in1);
       DcmParser p2 = pf.newDcmParser(in2);
       p1.setDcmHandler(attrs1.getDcmHandler());
       p2.setDcmHandler(attrs2.getDcmHandler());
       p1.parseDcmFile(FileFormat.DICOM_FILE, Tags.PixelData);
       p2.parseDcmFile(FileFormat.DICOM_FILE, Tags.PixelData);
       int samples = attrs1.getInt(Tags.SamplesPerPixel, 1);
       int frames = attrs1.getInt(Tags.NumberOfFrames, 1);
       int rows = attrs1.getInt(Tags.Rows, 1);
       int columns = attrs1.getInt(Tags.Columns, 1);
       int bitsAlloc = attrs1.getInt(Tags.BitsAllocated, 8);
       int bitsStored = attrs1.getInt(Tags.BitsStored, bitsAlloc);
       int bitsStored2 = attrs2.getInt(Tags.BitsStored, bitsAlloc);
       int pixelRepresentation1 = attrs1.getInt(Tags.PixelRepresentation, 0);
       int pixelRepresentation2 = attrs2.getInt(Tags.PixelRepresentation, 0);
       int frameLength = rows * columns * samples * bitsAlloc / 8;
       int pixelDataLength = frameLength * frames;
       if (pixelDataLength > p1.getReadLength() || pixelDataLength > p2.getReadLength()) {
         return Integer.MAX_VALUE;
       }
       byte[] b1 = new byte[BUFFER_SIZE];
       byte[] b2 = new byte[BUFFER_SIZE];
       byte lsb1 = 0, lsb2 = 0;
       int w1, w2, len, len2;
       int bitmask1 = 0xffff >>> (bitsAlloc - bitsStored);
       int bitmask2 = 0xffff >>> (bitsAlloc - bitsStored2);
       int signed1 = pixelRepresentation1 != 0 ? (-1 & ~bitmask1) >> 1 : 0;
       int signed2 = pixelRepresentation2 != 0 ? (-1 & ~bitmask2) >> 1 : 0;
       int pos = 0;
       while (pos < pixelDataLength) {
         len = in1.read(b1, 0, Math.min(pixelDataLength - pos, BUFFER_SIZE));
         if (len < 0) // EOF
         return Integer.MAX_VALUE;
         int off = 0;
         while (off < len) {
           off += len2 = in2.read(b2, off, len - off);
           if (len2 < 0) // EOF
           return Integer.MAX_VALUE;
         }
         if (bitsAlloc == 8)
           for (int i = 0; i < len; i++, pos++)
             maxDiff = Math.max(maxDiff, Math.abs((b1[i] & 0xff) - (b2[i] & 0xff)));
         else {
           for (int i = 0; i < len; i++, pos++)
             // TODO assumes LE Byte Order
             if ((pos & 1) == 0) {
               lsb1 = b1[i];
               lsb2 = b2[i];
             } else {
               if (((w1 = ((b1[i] << 8) | (lsb1 & 0xff)) & bitmask1) & signed1) != 0)
                 w1 |= signed1;
               if (((w2 = ((b2[i] << 8) | (lsb2 & 0xff)) & bitmask2) & signed2) != 0)
                 w2 |= signed2;
               maxDiff = Math.max(maxDiff, Math.abs(w1 - w2));
             }
         }
       }
       return maxDiff;
     } finally {
       in2.close();
     }
   } finally {
     in1.close();
   }
 }
/**
 * @author [email protected]
 * @version $Revision: 8056 $ $Date: 2008-11-12 14:31:15 +0100 (Wed, 12 Nov 2008) $
 * @since 27.12.2005
 * @ejb.bean name="PrivateManager" type="Stateless" view-type="remote"
 *     jndi-name="ejb/PrivateManager"
 * @ejb.transaction-type type="Container"
 * @ejb.transaction type="Required"
 * @ejb.ejb-ref ejb-name="Patient" view-type="local" ref-name="ejb/Patient"
 * @ejb.ejb-ref ejb-name="Study" view-type="local" ref-name="ejb/Study"
 * @ejb.ejb-ref ejb-name="Series" view-type="local" ref-name="ejb/Series"
 * @ejb.ejb-ref ejb-name="Instance" view-type="local" ref-name="ejb/Instance"
 * @ejb.ejb-ref ejb-name="File" view-type="local" ref-name="ejb/File"
 * @ejb.ejb-ref ejb-name="PrivatePatient" view-type="local" ref-name="ejb/PrivatePatient"
 * @ejb.ejb-ref ejb-name="PrivateStudy" view-type="local" ref-name="ejb/PrivateStudy"
 * @ejb.ejb-ref ejb-name="PrivateSeries" view-type="local" ref-name="ejb/PrivateSeries"
 * @ejb.ejb-ref ejb-name="PrivateInstance" view-type="local" ref-name="ejb/PrivateInstance"
 * @ejb.ejb-ref ejb-name="PrivateFile" view-type="local" ref-name="ejb/PrivateFile"
 */
public abstract class PrivateManagerBean implements SessionBean {

  private static final int DELETED = 1;

  private PatientLocalHome patHome;
  private StudyLocalHome studyHome;
  private SeriesLocalHome seriesHome;
  private InstanceLocalHome instHome;

  private PrivatePatientLocalHome privPatHome;
  private PrivateStudyLocalHome privStudyHome;
  private PrivateSeriesLocalHome privSeriesHome;
  private PrivateInstanceLocalHome privInstHome;
  private PrivateFileLocalHome privFileHome;

  private static final DcmObjectFactory dof = DcmObjectFactory.getInstance();

  private static Logger log = Logger.getLogger(PrivateManagerBean.class.getName());

  public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException {
    Context jndiCtx = null;
    try {
      jndiCtx = new InitialContext();
      patHome = (PatientLocalHome) jndiCtx.lookup("java:comp/env/ejb/Patient");
      studyHome = (StudyLocalHome) jndiCtx.lookup("java:comp/env/ejb/Study");
      seriesHome = (SeriesLocalHome) jndiCtx.lookup("java:comp/env/ejb/Series");
      instHome = (InstanceLocalHome) jndiCtx.lookup("java:comp/env/ejb/Instance");

      privPatHome = (PrivatePatientLocalHome) jndiCtx.lookup("java:comp/env/ejb/PrivatePatient");
      privStudyHome = (PrivateStudyLocalHome) jndiCtx.lookup("java:comp/env/ejb/PrivateStudy");
      privSeriesHome = (PrivateSeriesLocalHome) jndiCtx.lookup("java:comp/env/ejb/PrivateSeries");
      privInstHome = (PrivateInstanceLocalHome) jndiCtx.lookup("java:comp/env/ejb/PrivateInstance");
      privFileHome = (PrivateFileLocalHome) jndiCtx.lookup("java:comp/env/ejb/PrivateFile");
    } catch (NamingException e) {
      throw new EJBException(e);
    } finally {
      if (jndiCtx != null) {
        try {
          jndiCtx.close();
        } catch (NamingException ignore) {
        }
      }
    }
  }

  public void unsetSessionContext() {
    patHome = null;
    studyHome = null;
    seriesHome = null;
    instHome = null;
    privPatHome = null;
    privStudyHome = null;
    privSeriesHome = null;
    privInstHome = null;
    privFileHome = null;
  }

  /**
   * @throws FinderException
   * @ejb.interface-method
   */
  public void deletePrivateSeries(long series_pk) throws RemoteException, FinderException {
    try {
      PrivateSeriesLocal series = privSeriesHome.findByPrimaryKey(new Long(series_pk));
      PrivateStudyLocal study = series.getStudy();
      series.remove();
      if (study.getSeries().isEmpty()) {
        PrivatePatientLocal pat = study.getPatient();
        study.remove();
        if (pat.getStudies().isEmpty()) {
          pat.remove();
        }
      }
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /**
   * @throws FinderException
   * @ejb.interface-method
   */
  public Collection deletePrivateStudy(long study_pk) throws RemoteException, FinderException {
    try {
      PrivateStudyLocal study = privStudyHome.findByPrimaryKey(new Long(study_pk));
      ArrayList files = null;
      PrivatePatientLocal pat = study.getPatient();
      study.remove();
      if (pat.getStudies().isEmpty()) {
        pat.remove();
      }
      return files;
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /** @ejb.interface-method */
  public void deletePrivatePatient(long patient_pk) throws RemoteException {
    try {
      privPatHome.remove(new Long(patient_pk));
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /**
   * @throws FinderException
   * @throws FinderException
   * @ejb.interface-method
   */
  public void deletePrivateInstance(long instance_pk) throws RemoteException, FinderException {
    try {
      PrivateInstanceLocal instance = privInstHome.findByPrimaryKey(new Long(instance_pk));
      PrivateSeriesLocal series = instance.getSeries();
      instance.remove();
      if (series.getInstances().isEmpty()) {
        PrivateStudyLocal study = series.getStudy();
        series.remove();
        if (study.getSeries().isEmpty()) {
          PrivatePatientLocal pat = study.getPatient();
          study.remove();
          if (pat.getStudies().isEmpty()) {
            pat.remove();
          }
        }
      }
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /**
   * @throws FinderException
   * @ejb.interface-method
   */
  public void deletePrivateFile(long file_pk) throws RemoteException {
    try {
      privFileHome.remove(new Long(file_pk));
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /**
   * @throws FinderException
   * @ejb.interface-method
   */
  public void deletePrivateFiles(Collection fileDTOs) throws RemoteException {
    try {
      for (Iterator iter = fileDTOs.iterator(); iter.hasNext(); ) {
        privFileHome.remove(new Long(((FileDTO) iter.next()).getPk()));
      }
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /** @ejb.interface-method */
  public void deleteAll(int privateType) throws RemoteException {
    try {
      Collection c = privPatHome.findByPrivateType(privateType);
      for (Iterator iter = c.iterator(); iter.hasNext(); ) {
        privPatHome.remove(((PrivatePatientLocal) iter.next()).getPk());
      }
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    } catch (FinderException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /**
   * Delete a list of instances, i.e., move them to trash bin
   *
   * @ejb.interface-method
   * @param iuids A list of instance uid
   * @param cascading True to delete the series/study if there's no instance/series
   * @return a collection of Dataset containing the actuall detetion information per study
   * @throws RemoteException
   */
  public Collection moveInstancesToTrash(String[] iuids, boolean cascading) throws RemoteException {
    try {
      // These instances may belong to multiple studies,
      // although mostly they should be the same study
      Map mapStudies = new HashMap();
      for (int i = 0; i < iuids.length; i++) {
        InstanceLocal instance = instHome.findBySopIuid(iuids[i]);
        SeriesLocal series = instance.getSeries();
        StudyLocal study = series.getStudy();
        if (!mapStudies.containsKey(study)) mapStudies.put(study, new HashMap());
        Map mapSeries = (Map) mapStudies.get(study);
        if (!mapSeries.containsKey(series)) mapSeries.put(series, new ArrayList());
        Collection colInstances = (Collection) mapSeries.get(series);
        colInstances.add(instance);
      }

      List dss = new ArrayList();
      Iterator iter = mapStudies.keySet().iterator();
      while (iter.hasNext()) {
        StudyLocal study = (StudyLocal) iter.next();
        dss.add(getStudyMgtDataset(study, (Map) mapStudies.get(study)));
        Iterator iter2 = ((Map) mapStudies.get(study)).keySet().iterator();
        while (iter2.hasNext()) {
          SeriesLocal series = (SeriesLocal) iter2.next();
          List instances = (List) ((Map) mapStudies.get(study)).get(series);
          for (int i = 0; i < instances.size(); i++) {
            // Delete the instance now, i.e., move to trash bin,
            // becoming private instance
            getPrivateInstance((InstanceLocal) instances.get(i), DELETED, null);
            ((InstanceLocal) instances.get(i)).remove();
          }
          if (series.getInstances().size() == 0 && cascading) {
            // Delete the series too since there's no instance left
            getPrivateSeries(series, DELETED, null, false);
            series.remove();
          } else UpdateDerivedFieldsUtils.updateDerivedFieldsOf(series);
        }
        if (study.getSeries().size() == 0 && cascading) {
          // Delete the study too since there's no series left
          getPrivateStudy(study, DELETED, null, false);
          study.remove();
        } else UpdateDerivedFieldsUtils.updateDerivedFieldsOf(study);
      }

      return dss;
    } catch (CreateException e) {
      throw new RemoteException(e.getMessage());
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (FinderException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /** @ejb.interface-method */
  public Dataset moveInstanceToTrash(long instance_pk) throws RemoteException {
    try {
      InstanceLocal instance = instHome.findByPrimaryKey(new Long(instance_pk));
      Collection colInstance = new ArrayList();
      colInstance.add(instance);
      SeriesLocal series = instance.getSeries();
      Map mapSeries = new HashMap();
      mapSeries.put(series, colInstance);
      Dataset ds = getStudyMgtDataset(series.getStudy(), mapSeries);
      getPrivateInstance(instance, DELETED, null);
      instance.remove();
      UpdateDerivedFieldsUtils.updateDerivedFieldsOf(series);
      UpdateDerivedFieldsUtils.updateDerivedFieldsOf(series.getStudy());
      return ds;
    } catch (CreateException e) {
      throw new RemoteException(e.getMessage());
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (FinderException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /** @ejb.interface-method */
  public Dataset moveSeriesToTrash(long series_pk) throws RemoteException {
    try {
      SeriesLocal series = seriesHome.findByPrimaryKey(new Long(series_pk));
      StudyLocal study = series.getStudy();
      Map mapSeries = new HashMap();
      mapSeries.put(series, series.getInstances());
      Dataset ds = getStudyMgtDataset(series.getStudy(), mapSeries);
      getPrivateSeries(series, DELETED, null, true);
      series.remove();
      UpdateDerivedFieldsUtils.updateDerivedFieldsOf(study);
      return ds;
    } catch (CreateException e) {
      throw new RemoteException(e.getMessage());
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (FinderException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /** @ejb.interface-method */
  public Collection moveSeriesOfPPSToTrash(String ppsIUID, boolean removeEmptyParents)
      throws RemoteException {
    Collection result = new ArrayList(); // FIXME: NOT IN USE
    try {
      Object[] ppsSeries = seriesHome.findByPpsIuid(ppsIUID).toArray();
      if (ppsSeries.length > 0) {
        SeriesLocal series = null;
        StudyLocal study = ((SeriesLocal) ppsSeries[0]).getStudy();
        for (int i = 0; i < ppsSeries.length; i++) {
          series = (SeriesLocal) ppsSeries[i];
          getPrivateSeries(series, DELETED, null, true);
          series.remove();
        }
        if (removeEmptyParents && study.getSeries().isEmpty()) {
          study.remove();
        } else {
          UpdateDerivedFieldsUtils.updateDerivedFieldsOf(study);
        }
      }
    } catch (FinderException ignore) {
    } catch (Exception e) {
      throw new RemoteException(e.getMessage());
    }
    return result;
  }

  /** @ejb.interface-method */
  public Dataset moveStudyToTrash(String iuid) throws RemoteException {
    try {
      StudyLocal study = studyHome.findByStudyIuid(iuid);
      if (study != null) return moveStudyToTrash(study.getPk().longValue());
      else return null;
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (FinderException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /** @ejb.interface-method */
  public Dataset moveStudyToTrash(long study_pk) throws RemoteException {
    try {
      StudyLocal study = studyHome.findByPrimaryKey(new Long(study_pk));
      Dataset ds = getStudyMgtDataset(study, null);
      getPrivateStudy(study, DELETED, null, true);
      study.remove();
      return ds;
    } catch (CreateException e) {
      throw new RemoteException(e.getMessage());
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (FinderException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  /** @ejb.interface-method */
  public Collection movePatientToTrash(long pat_pk) throws RemoteException {
    try {
      PatientLocal patient = patHome.findByPrimaryKey(new Long(pat_pk));
      Collection col = patient.getStudies();
      Collection result = new ArrayList();
      for (Iterator iter = col.iterator(); iter.hasNext(); ) {
        result.add(getStudyMgtDataset((StudyLocal) iter.next(), null));
      }
      Dataset ds = patient.getAttributes(true);
      getPrivatePatient(patient, DELETED, true);
      patient.remove();
      if (result.isEmpty()) result.add(ds);
      return result;
    } catch (CreateException e) {
      throw new RemoteException(e.getMessage());
    } catch (EJBException e) {
      throw new RemoteException(e.getMessage());
    } catch (FinderException e) {
      throw new RemoteException(e.getMessage());
    } catch (RemoveException e) {
      throw new RemoteException(e.getMessage());
    }
  }

  private PrivateInstanceLocal getPrivateInstance(
      InstanceLocal instance, int type, PrivateSeriesLocal privSeries)
      throws FinderException, CreateException {
    Collection col = privInstHome.findBySopIuid(type, instance.getSopIuid());
    PrivateInstanceLocal privInstance;
    if (col.isEmpty()) {
      if (privSeries == null) {
        privSeries = getPrivateSeries(instance.getSeries(), type, null, false);
      }
      privInstance = privInstHome.create(type, instance.getAttributes(true), privSeries);
    } else {
      privInstance = (PrivateInstanceLocal) col.iterator().next();
    }
    Object[] files = instance.getFiles().toArray();
    FileLocal file;
    for (int i = 0; i < files.length; i++) {
      file = (FileLocal) files[i];
      privFileHome.create(
          file.getFilePath(),
          file.getFileTsuid(),
          file.getFileSize(),
          file.getFileMd5(),
          file.getFileStatus(),
          privInstance,
          file.getFileSystem());
      try {
        file.remove();
      } catch (Exception x) {
        log.warn("Can not remove File record:" + file, x);
      }
    }
    return privInstance;
  }

  private PrivateSeriesLocal getPrivateSeries(
      SeriesLocal series, int type, PrivateStudyLocal privStudy, boolean includeInstances)
      throws FinderException, CreateException {
    Collection col = privSeriesHome.findBySeriesIuid(type, series.getSeriesIuid());
    PrivateSeriesLocal privSeries;
    if (col.isEmpty()) {
      if (privStudy == null) {
        privStudy = getPrivateStudy(series.getStudy(), type, null, false);
      }
      privSeries = privSeriesHome.create(type, series.getAttributes(true), privStudy);
    } else {
      privSeries = (PrivateSeriesLocal) col.iterator().next();
    }
    if (includeInstances) {
      for (Iterator iter = series.getInstances().iterator(); iter.hasNext(); ) {
        getPrivateInstance(
            (InstanceLocal) iter.next(), type, privSeries); // move also all instances
      }
    }
    return privSeries;
  }

  private PrivateStudyLocal getPrivateStudy(
      StudyLocal study, int type, PrivatePatientLocal privPat, boolean includeSeries)
      throws FinderException, CreateException {
    Collection col = privStudyHome.findByStudyIuid(type, study.getStudyIuid());
    PrivateStudyLocal privStudy;
    if (col.isEmpty()) {
      if (privPat == null) {
        privPat = getPrivatePatient(study.getPatient(), type, false);
      }
      privStudy = privStudyHome.create(type, study.getAttributes(true), privPat);
    } else {
      privStudy = (PrivateStudyLocal) col.iterator().next();
    }
    if (includeSeries) {
      for (Iterator iter = study.getSeries().iterator(); iter.hasNext(); ) {
        getPrivateSeries(
            (SeriesLocal) iter.next(), type, privStudy, true); // move also all instances
      }
    }
    return privStudy;
  }

  private PrivatePatientLocal getPrivatePatient(
      PatientLocal patient, int type, boolean includeStudies)
      throws FinderException, CreateException {
    Collection col =
        privPatHome.findByPatientIdWithIssuer(
            type, patient.getPatientId(), patient.getIssuerOfPatientId());
    PrivatePatientLocal privPat;
    if (col.isEmpty()) {
      privPat = privPatHome.create(type, patient.getAttributes(true));
    } else {
      privPat = (PrivatePatientLocal) col.iterator().next();
    }
    if (includeStudies) {
      for (Iterator iter = patient.getStudies().iterator(); iter.hasNext(); ) {
        getPrivateStudy((StudyLocal) iter.next(), type, privPat, true); // move
        // also
        // all
        // instances
      }
    }
    return privPat;
  }

  private Dataset getStudyMgtDataset(StudyLocal study, Map mapSeries) {
    Dataset ds = dof.newDataset();
    ds.putUI(Tags.StudyInstanceUID, study.getStudyIuid());
    ds.putOB(PrivateTags.StudyPk, Convert.toBytes(study.getPk().longValue()));
    ds.putSH(Tags.AccessionNumber, study.getAccessionNumber());
    ds.putLO(Tags.PatientID, study.getPatient().getPatientId());
    ds.putLO(Tags.IssuerOfPatientID, study.getPatient().getIssuerOfPatientId());
    ds.putPN(Tags.PatientName, study.getPatient().getPatientName());

    log.debug("getStudyMgtDataset: studyIUID:" + study.getStudyIuid());
    DcmElement refSeriesSeq = ds.putSQ(Tags.RefSeriesSeq);

    Iterator iter =
        (mapSeries == null) ? study.getSeries().iterator() : mapSeries.keySet().iterator();
    while (iter.hasNext()) {
      SeriesLocal sl = (SeriesLocal) iter.next();
      Dataset dsSer = refSeriesSeq.addNewItem();
      dsSer.putUI(Tags.SeriesInstanceUID, sl.getSeriesIuid());
      Collection instances =
          (mapSeries == null) ? sl.getInstances() : (Collection) mapSeries.get(sl);
      Iterator iter2 = instances.iterator();
      DcmElement refSopSeq = null;
      if (iter2.hasNext()) refSopSeq = dsSer.putSQ(Tags.RefSOPSeq);
      while (iter2.hasNext()) {
        InstanceLocal il = (InstanceLocal) iter2.next();
        Dataset dsInst = refSopSeq.addNewItem();
        dsInst.putUI(Tags.RefSOPClassUID, il.getSopCuid());
        dsInst.putUI(Tags.RefSOPInstanceUID, il.getSopIuid());
        dsInst.putAE(Tags.RetrieveAET, il.getRetrieveAETs());
      }
    }
    if (log.isDebugEnabled()) {
      log.debug("return StgMgtDataset:");
      log.debug(ds);
    }
    return ds;
  }
}