diff --git a/dicoogle/src/main/java/pt/ua/dicoogle/server/DicomStorage.java b/dicoogle/src/main/java/pt/ua/dicoogle/server/DicomStorage.java
index a26975c9e..e6f87e9e3 100755
--- a/dicoogle/src/main/java/pt/ua/dicoogle/server/DicomStorage.java
+++ b/dicoogle/src/main/java/pt/ua/dicoogle/server/DicomStorage.java
@@ -1,383 +1,401 @@
-/**
- * Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
- *
- * This file is part of Dicoogle/dicoogle.
- *
- * Dicoogle/dicoogle is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dicoogle/dicoogle is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Dicoogle. If not, see .
- */
-package pt.ua.dicoogle.server;
-
-import pt.ua.dicoogle.core.settings.ServerSettingsManager;
-
-import java.io.IOException;
-import java.net.URI;
-import java.util.*;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.dcm4che2.data.DicomObject;
-import org.dcm4che2.data.Tag;
-import org.dcm4che2.data.UID;
-import org.dcm4che2.net.Association;
-import org.dcm4che2.net.CommandUtils;
-import org.dcm4che2.net.Device;
-import org.dcm4che2.net.DicomServiceException;
-
-import org.slf4j.LoggerFactory;
-
-import org.dcm4che2.net.NetworkApplicationEntity;
-import org.dcm4che2.net.NetworkConnection;
-import org.dcm4che2.net.NewThreadExecutor;
-import org.dcm4che2.net.PDVInputStream;
-import org.dcm4che2.net.Status;
-import org.dcm4che2.net.TransferCapability;
-import org.dcm4che2.net.service.StorageService;
-import org.dcm4che2.net.service.VerificationService;
-
-
-import pt.ua.dicoogle.plugins.PluginController;
-import pt.ua.dicoogle.sdk.IndexerInterface;
-import pt.ua.dicoogle.sdk.StorageInterface;
-import pt.ua.dicoogle.sdk.settings.server.ServerSettings;
-
-
-/**
- * DICOM Storage Service is provided by this class
- *
- * @author Luís Bastião Silva
- * @author Marco Pereira
- */
-
-public class DicomStorage extends StorageService {
-
- private SOPList list;
- private ServerSettings settings;
-
- private Executor executor = new NewThreadExecutor("DicoogleStorage");
- private Device device = new Device("DicoogleStorage");
- private NetworkApplicationEntity nae = new NetworkApplicationEntity();
- private NetworkConnection nc = new NetworkConnection();
-
- private String path;
-
- private Set alternativeAETs = new HashSet<>();
- private Set priorityAETs = new HashSet<>();
-
- // Changed to support priority queue.
- private BlockingQueue queue = new PriorityBlockingQueue<>();
- private NetworkApplicationEntity[] naeArr = null;
- private AtomicLong seqNum = new AtomicLong(0L);
-
- /**
- *
- * @param Services List of supported SOP Classes
- * @param l list of Supported SOPClasses with supported Transfer Syntax
- */
-
- public DicomStorage(String[] Services, SOPList l) {
- // just because the call to super must be the first instruction
- super(Services);
-
- // our configuration format
- list = l;
- settings = ServerSettingsManager.getSettings();
-
- // Added default alternative AETitle.
- alternativeAETs.add(ServerSettingsManager.getSettings().getArchiveSettings().getNodeName());
-
- path = settings.getArchiveSettings().getMainDirectory();
- if (path == null) {
- path = "/dev/null";
- }
-
- this.priorityAETs = new HashSet<>(settings.getDicomServicesSettings().getPriorityAETitles());
- LoggerFactory.getLogger(DicomStorage.class).debug("Priority C-STORE: {}", this.priorityAETs);
-
- device.setNetworkApplicationEntity(nae);
-
- device.setNetworkConnection(nc);
- nae.setNetworkConnection(nc);
-
- // we accept assoociations, this is a server
- nae.setAssociationAcceptor(true);
- // we support the VerificationServiceSOP
- nae.register(new VerificationService());
- // and the StorageServiceSOP
- nae.register(this);
-
- nae.setAETitle(settings.getDicomServicesSettings().getAETitle());
-
-
- nc.setPort(settings.getDicomServicesSettings().getStorageSettings().getPort());
-
-
- this.nae.setInstalled(true);
- this.nc.setMaxScpAssociations(Integer.parseInt(System.getProperty("dicoogle.cstore.maxScpAssociations", "50")));
- this.nc.setAcceptTimeout(Integer.parseInt(System.getProperty("dicoogle.cstore.acceptTimeout", "5000")));
- this.nc.setConnectTimeout(Integer.parseInt(System.getProperty("dicoogle.cstore.connectTimeout", "5000")));
-
- this.nae.setAssociationAcceptor(true);
- this.nae.setAssociationInitiator(false);
-
- int maxPDULengthReceive =
- settings.getDicomServicesSettings().getQueryRetrieveSettings().getMaxPDULengthReceive();
- int maxPDULengthSend = settings.getDicomServicesSettings().getQueryRetrieveSettings().getMaxPDULengthSend();
-
- this.nae.setDimseRspTimeout(
- Integer.parseInt(System.getProperty("dicoogle.cstore.dimseRspTimeout", "18000000")));
- this.nae.setIdleTimeout(Integer.parseInt(System.getProperty("dicoogle.cstore.idleTimeout", "18000000")));
- this.nae.setMaxPDULengthReceive(
- maxPDULengthReceive + Integer.parseInt(System.getProperty("dicoogle.cstore.appendMaxPDU", "1000")));
- this.nae.setMaxPDULengthSend(
- maxPDULengthSend + Integer.parseInt(System.getProperty("dicoogle.cstore.appendMaxPDU", "1000")));
- this.nae.setRetrieveRspTimeout(
- Integer.parseInt(System.getProperty("dicoogle.cstore.retrieveRspTimeout", "18000000")));
-
-
- // Added alternative AETitles.
-
- naeArr = new NetworkApplicationEntity[alternativeAETs.size() + 1];
- // Just adding the first AETitle
- naeArr[0] = nae;
-
- int k = 1;
-
- for (String alternativeAET : alternativeAETs) {
- NetworkApplicationEntity nae2 = new NetworkApplicationEntity();
- nae2.setNetworkConnection(nc);
- nae2.setDimseRspTimeout(
- Integer.parseInt(System.getProperty("dicoogle.cstore.dimseRspTimeout", "18000000")));
- nae2.setIdleTimeout(Integer.parseInt(System.getProperty("dicoogle.cstore.idleTimeout", "18000000")));
- nae2.setMaxPDULengthReceive(
- maxPDULengthReceive + Integer.parseInt(System.getProperty("dicoogle.cstore.appendMaxPDU", "1000")));
- nae2.setMaxPDULengthSend(
- maxPDULengthSend + Integer.parseInt(System.getProperty("dicoogle.cstore.appendMaxPDU", "1000")));
- nae2.setRetrieveRspTimeout(
- Integer.parseInt(System.getProperty("dicoogle.cstore.retrieveRspTimeout", "18000000")));
-
- // we accept assoociations, this is a server
- nae2.setAssociationAcceptor(true);
- // we support the VerificationServiceSOP
- nae2.register(new VerificationService());
- // and the StorageServiceSOP
- nae2.register(this);
- nae2.setAETitle(alternativeAET);
- ServerSettings settings = ServerSettingsManager.getSettings();
- Collection caet = settings.getDicomServicesSettings().getAllowedAETitles();
- if (!caet.isEmpty()) {
- String[] array = new String[caet.size()];
- caet.toArray(array);
- nae2.setPreferredCallingAETitle(array);
- }
- naeArr[k] = nae2;
- k++;
-
- }
-
- // Just set the Network Application Entity array - which accepts a set of AEs.
- device.setNetworkApplicationEntity(naeArr);
-
-
- initTS(Services);
- }
-
- /**
- * Sets the tranfer capability for this execution of the storage service
- * @param Services Services to be supported
- */
- private void initTS(String[] Services) {
- int count = list.getAccepted();
- // System.out.println(count);
- TransferCapability[] tc = new TransferCapability[count + 1];
- String[] Verification = {UID.ImplicitVRLittleEndian, UID.ExplicitVRLittleEndian, UID.ExplicitVRBigEndian};
- String[] TS;
- TransfersStorage local;
-
- tc[0] = new TransferCapability(UID.VerificationSOPClass, Verification, TransferCapability.SCP);
- int j = 0;
- for (int i = 0; i < Services.length; i++) {
- count = 0;
- local = list.getTS(Services[i]);
- if (local.getAccepted()) {
- TS = local.getVerboseTS();
- if (TS != null) {
-
- tc[j + 1] = new TransferCapability(Services[i], TS, TransferCapability.SCP);
- j++;
- }
- }
- }
-
- // Setting the TS in all NetworkApplicationEntitys
- for (int i = 0; i < naeArr.length; i++) {
-
- naeArr[i].setTransferCapability(tc);
- }
- nae.setTransferCapability(tc);
- }
-
- @Override
- /**
- * Called when a C-Store Request has been accepted
- * Parameters defined by dcm4che2
- */
- public void cstore(final Association as, final int pcid, DicomObject rq, PDVInputStream dataStream, String tsuid)
- throws DicomServiceException, IOException {
- // DebugManager.getSettings().debug(":: Verify Permited AETs @??C-Store Request ");
-
- boolean permited = false;
- Collection allowedAETitles =
- ServerSettingsManager.getSettings().getDicomServicesSettings().getAllowedAETitles();
- if (allowedAETitles.isEmpty()) {
- permited = true;
- } else {
- permited = allowedAETitles.contains(as.getCallingAET());
- }
-
- if (!permited) {
- // DebugManager.getSettings().debug("Client association NOT permited: " + as.getCallingAET() + "!");
- LoggerFactory.getLogger(DicomStorage.class).warn("Client association with {} NOT permitted!", as.getCallingAET());
- as.abort();
-
- return;
- }
-
- final DicomObject rsp = CommandUtils.mkRSP(rq, CommandUtils.SUCCESS);
- onCStoreRQ(as, pcid, rq, dataStream, tsuid, rsp);
- as.writeDimseRSP(pcid, rsp);
- // onCStoreRSP(as, pcid, rq, dataStream, tsuid, rsp);
- }
-
- @Override
- /**
- * Actually do the job of saving received file on disk
- * on this server with extras such as Lucene indexing
- * and DICOMDIR update
- */
- protected void onCStoreRQ(Association as, int pcid, DicomObject rq, PDVInputStream dataStream, String tsuid,
- DicomObject rsp) throws IOException, DicomServiceException {
- try {
-
- String cuid = rq.getString(Tag.AffectedSOPClassUID);
- String iuid = rq.getString(Tag.AffectedSOPInstanceUID);
-
- DicomObject d = dataStream.readDataset();
-
- d.initFileMetaInformation(cuid, iuid, tsuid);
-
- Iterable plugins = PluginController.getInstance().getStoragePlugins(true);
-
- for (StorageInterface storage : plugins) {
- URI uri = storage.store(d);
- if (uri != null) {
- // enqueue to index
- ImageElement element = new ImageElement(uri, as.getCallingAET(), seqNum.getAndIncrement());
- queue.add(element);
- }
- }
-
- } catch (IOException e) {
- throw new DicomServiceException(rq, Status.ProcessingFailure, e.getMessage());
- }
- }
-
- /**
- * A C-STORE entry.
- * For Each C-STORE RQ, an ImageElement is created
- * and put in the storage service's priority queue for indexing.
- *
- * The priority criteria are, in descending order of importance:
- * 1. whether the calling AE title is in the list of priority AEs
- * 2. earliest sequence number
- *
- * This only happens after the store in Storage Plugins.
- */
- final class ImageElement implements Comparable {
- private final URI uri;
- private final String callingAET;
- private final long seqNumber;
-
- ImageElement(URI uri, String callingAET, long seqNumber) {
- Objects.requireNonNull(uri);
- Objects.requireNonNull(callingAET);
- this.uri = uri;
- this.callingAET = callingAET;
- this.seqNumber = seqNumber;
- }
-
- public URI getUri() {
- return uri;
- }
-
- public String getCallingAET() {
- return callingAET;
- }
-
- public long getSequenceNumber() {
- return seqNumber;
- }
-
- @Override
- public int compareTo(ImageElement other) {
- boolean thisPriority = priorityAETs.contains(this.getCallingAET());
- boolean thatPriority = priorityAETs.contains(other.getCallingAET());
-
- int priorityOrder = Boolean.compare(thisPriority, thatPriority);
-
- if (priorityOrder != 0) {
- return priorityOrder;
- }
-
- return Long.compare(this.seqNumber, other.seqNumber);
- }
- }
-
-
- class Indexer extends Thread {
- public Collection plugins;
-
- public void run() {
- while (true) {
- try {
- // Fetch an element by the queue taking into account the priorities.
- ImageElement element = queue.take();
- URI exam = element.getUri();
- PluginController.getInstance().indexBlocking(exam);
- } catch (InterruptedException ex) {
- LoggerFactory.getLogger(DicomStorage.class).error("Could not take instance to index", ex);
- }
-
- }
-
- }
- }
-
- private Indexer indexer = new Indexer();
-
- /*
- * Start the Storage Service
- * @throws java.io.IOException
- */
- public void start() throws IOException {
- device.startListening(executor);
- indexer.start();
- }
-
- /**
- * Stop the storage service
- */
- public void stop() {
- device.stopListening();
- }
-}
+/**
+ * Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
+ *
+ * This file is part of Dicoogle/dicoogle.
+ *
+ * Dicoogle/dicoogle is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dicoogle/dicoogle is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dicoogle. If not, see .
+ */
+package pt.ua.dicoogle.server;
+
+import pt.ua.dicoogle.core.settings.ServerSettingsManager;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.dcm4che2.data.DicomObject;
+import org.dcm4che2.data.Tag;
+import org.dcm4che2.data.UID;
+import org.dcm4che2.net.Association;
+import org.dcm4che2.net.CommandUtils;
+import org.dcm4che2.net.Device;
+import org.dcm4che2.net.DicomServiceException;
+
+import org.slf4j.LoggerFactory;
+
+import org.dcm4che2.net.NetworkApplicationEntity;
+import org.dcm4che2.net.NetworkConnection;
+import org.dcm4che2.net.NewThreadExecutor;
+import org.dcm4che2.net.PDVInputStream;
+import org.dcm4che2.net.Status;
+import org.dcm4che2.net.TransferCapability;
+import org.dcm4che2.net.service.StorageService;
+import org.dcm4che2.net.service.VerificationService;
+
+
+import pt.ua.dicoogle.plugins.PluginController;
+import pt.ua.dicoogle.sdk.IndexerInterface;
+import pt.ua.dicoogle.sdk.StorageInterface;
+import pt.ua.dicoogle.sdk.settings.server.ServerSettings;
+
+
+/**
+ * DICOM Storage Service is provided by this class
+ *
+ * @author Luís Bastião Silva
+ * @author Marco Pereira
+ */
+
+public class DicomStorage extends StorageService {
+
+ private SOPList list;
+ private ServerSettings settings;
+
+ private Executor executor = new NewThreadExecutor("DicoogleStorage");
+ private Device device = new Device("DicoogleStorage");
+ private NetworkApplicationEntity nae = new NetworkApplicationEntity();
+ private NetworkConnection nc = new NetworkConnection();
+
+ private String path;
+
+ private Set alternativeAETs = new HashSet<>();
+ private Set priorityAETs = new HashSet<>();
+
+ // Changed to support priority queue.
+ private BlockingQueue queue = new PriorityBlockingQueue<>();
+ private NetworkApplicationEntity[] naeArr = null;
+ private AtomicLong seqNum = new AtomicLong(0L);
+
+ /**
+ *
+ * @param Services List of supported SOP Classes
+ * @param l list of Supported SOPClasses with supported Transfer Syntax
+ */
+
+ public DicomStorage(String[] Services, SOPList l) {
+ // just because the call to super must be the first instruction
+ super(Services);
+
+ // our configuration format
+ list = l;
+ settings = ServerSettingsManager.getSettings();
+
+ // Added default alternative AETitle.
+ alternativeAETs.add(ServerSettingsManager.getSettings().getArchiveSettings().getNodeName());
+
+ path = settings.getArchiveSettings().getMainDirectory();
+ if (path == null) {
+ path = "/dev/null";
+ }
+
+ this.priorityAETs = new HashSet<>(settings.getDicomServicesSettings().getPriorityAETitles());
+ LoggerFactory.getLogger(DicomStorage.class).debug("Priority C-STORE: {}", this.priorityAETs);
+
+ device.setNetworkApplicationEntity(nae);
+
+ device.setNetworkConnection(nc);
+ nae.setNetworkConnection(nc);
+
+ // we accept assoociations, this is a server
+ nae.setAssociationAcceptor(true);
+ // we support the VerificationServiceSOP
+ nae.register(new VerificationService());
+ // and the StorageServiceSOP
+ nae.register(this);
+
+ nae.setAETitle(settings.getDicomServicesSettings().getAETitle());
+
+
+ nc.setPort(settings.getDicomServicesSettings().getStorageSettings().getPort());
+
+
+ this.nae.setInstalled(true);
+ this.nc.setMaxScpAssociations(Integer.parseInt(System.getProperty("dicoogle.cstore.maxScpAssociations", "50")));
+ this.nc.setAcceptTimeout(Integer.parseInt(System.getProperty("dicoogle.cstore.acceptTimeout", "5000")));
+ this.nc.setConnectTimeout(Integer.parseInt(System.getProperty("dicoogle.cstore.connectTimeout", "5000")));
+
+ this.nae.setAssociationAcceptor(true);
+ this.nae.setAssociationInitiator(false);
+
+ int maxPDULengthReceive =
+ settings.getDicomServicesSettings().getQueryRetrieveSettings().getMaxPDULengthReceive();
+ int maxPDULengthSend = settings.getDicomServicesSettings().getQueryRetrieveSettings().getMaxPDULengthSend();
+
+ this.nae.setDimseRspTimeout(
+ Integer.parseInt(System.getProperty("dicoogle.cstore.dimseRspTimeout", "18000000")));
+ this.nae.setIdleTimeout(Integer.parseInt(System.getProperty("dicoogle.cstore.idleTimeout", "18000000")));
+ this.nae.setMaxPDULengthReceive(
+ maxPDULengthReceive + Integer.parseInt(System.getProperty("dicoogle.cstore.appendMaxPDU", "1000")));
+ this.nae.setMaxPDULengthSend(
+ maxPDULengthSend + Integer.parseInt(System.getProperty("dicoogle.cstore.appendMaxPDU", "1000")));
+ this.nae.setRetrieveRspTimeout(
+ Integer.parseInt(System.getProperty("dicoogle.cstore.retrieveRspTimeout", "18000000")));
+
+
+ // Added alternative AETitles.
+
+ naeArr = new NetworkApplicationEntity[alternativeAETs.size() + 1];
+ // Just adding the first AETitle
+ naeArr[0] = nae;
+
+ int k = 1;
+
+ for (String alternativeAET : alternativeAETs) {
+ NetworkApplicationEntity nae2 = new NetworkApplicationEntity();
+ nae2.setNetworkConnection(nc);
+ nae2.setDimseRspTimeout(
+ Integer.parseInt(System.getProperty("dicoogle.cstore.dimseRspTimeout", "18000000")));
+ nae2.setIdleTimeout(Integer.parseInt(System.getProperty("dicoogle.cstore.idleTimeout", "18000000")));
+ nae2.setMaxPDULengthReceive(
+ maxPDULengthReceive + Integer.parseInt(System.getProperty("dicoogle.cstore.appendMaxPDU", "1000")));
+ nae2.setMaxPDULengthSend(
+ maxPDULengthSend + Integer.parseInt(System.getProperty("dicoogle.cstore.appendMaxPDU", "1000")));
+ nae2.setRetrieveRspTimeout(
+ Integer.parseInt(System.getProperty("dicoogle.cstore.retrieveRspTimeout", "18000000")));
+
+ // we accept assoociations, this is a server
+ nae2.setAssociationAcceptor(true);
+ // we support the VerificationServiceSOP
+ nae2.register(new VerificationService());
+ // and the StorageServiceSOP
+ nae2.register(this);
+ nae2.setAETitle(alternativeAET);
+ ServerSettings settings = ServerSettingsManager.getSettings();
+ Collection caet = settings.getDicomServicesSettings().getAllowedAETitles();
+ if (!caet.isEmpty()) {
+ String[] array = new String[caet.size()];
+ caet.toArray(array);
+ nae2.setPreferredCallingAETitle(array);
+ }
+ naeArr[k] = nae2;
+ k++;
+
+ }
+
+ // Just set the Network Application Entity array - which accepts a set of AEs.
+ device.setNetworkApplicationEntity(naeArr);
+
+
+ initTS(Services);
+ }
+
+ /**
+ * Sets the tranfer capability for this execution of the storage service
+ * @param Services Services to be supported
+ */
+ private void initTS(String[] Services) {
+ int count = list.getAccepted();
+ // System.out.println(count);
+ TransferCapability[] tc = new TransferCapability[count + 1];
+ String[] Verification = {UID.ImplicitVRLittleEndian, UID.ExplicitVRLittleEndian, UID.ExplicitVRBigEndian};
+ String[] TS;
+ TransfersStorage local;
+
+ tc[0] = new TransferCapability(UID.VerificationSOPClass, Verification, TransferCapability.SCP);
+ int j = 0;
+ for (int i = 0; i < Services.length; i++) {
+ count = 0;
+ local = list.getTS(Services[i]);
+ if (local.getAccepted()) {
+ TS = local.getVerboseTS();
+ if (TS != null) {
+
+ tc[j + 1] = new TransferCapability(Services[i], TS, TransferCapability.SCP);
+ j++;
+ }
+ }
+ }
+
+ // Setting the TS in all NetworkApplicationEntitys
+ for (int i = 0; i < naeArr.length; i++) {
+
+ naeArr[i].setTransferCapability(tc);
+ }
+ nae.setTransferCapability(tc);
+ }
+
+ @Override
+ /**
+ * Called when a C-Store Request has been accepted
+ * Parameters defined by dcm4che2
+ */
+ public void cstore(final Association as, final int pcid, DicomObject rq, PDVInputStream dataStream, String tsuid)
+ throws DicomServiceException, IOException {
+ // DebugManager.getSettings().debug(":: Verify Permited AETs @??C-Store Request ");
+
+ boolean permited = false;
+ Collection allowedAETitles =
+ ServerSettingsManager.getSettings().getDicomServicesSettings().getAllowedAETitles();
+ if (allowedAETitles.isEmpty()) {
+ permited = true;
+ } else {
+ permited = allowedAETitles.contains(as.getCallingAET());
+ }
+
+ if (!permited) {
+ // DebugManager.getSettings().debug("Client association NOT permited: " + as.getCallingAET() + "!");
+ LoggerFactory.getLogger(DicomStorage.class).warn("Client association with {} NOT permitted!", as.getCallingAET());
+ as.abort();
+
+ return;
+ }
+
+ final DicomObject rsp = CommandUtils.mkRSP(rq, CommandUtils.SUCCESS);
+ onCStoreRQ(as, pcid, rq, dataStream, tsuid, rsp);
+ as.writeDimseRSP(pcid, rsp);
+ // onCStoreRSP(as, pcid, rq, dataStream, tsuid, rsp);
+ }
+
+ @Override
+ /**
+ * Actually do the job of saving received file on disk
+ * on this server with extras such as Lucene indexing
+ * and DICOMDIR update
+ */
+ protected void onCStoreRQ(Association as, int pcid, DicomObject rq, PDVInputStream dataStream, String tsuid,
+ DicomObject rsp) throws IOException, DicomServiceException {
+ try {
+
+ String cuid = rq.getString(Tag.AffectedSOPClassUID);
+ String iuid = rq.getString(Tag.AffectedSOPInstanceUID);
+
+ DicomObject d = dataStream.readDataset();
+
+ d.initFileMetaInformation(cuid, iuid, tsuid);
+
+ Iterable plugins = PluginController.getInstance().getStoragePlugins(true);
+
+ for (StorageInterface storage : plugins) {
+ URI uri = storage.store(d);
+ if (uri != null) {
+ // enqueue to index
+ ImageElement element = new ImageElement(uri, as.getCallingAET(), seqNum.getAndIncrement());
+ queue.add(element);
+ }
+ }
+
+ } catch (IOException e) {
+ throw new DicomServiceException(rq, Status.ProcessingFailure, e.getMessage());
+ }
+ }
+
+ /**
+ * A C-STORE entry.
+ * For Each C-STORE RQ, an ImageElement is created
+ * and put in the storage service's priority queue for indexing.
+ *
+ * The priority criteria are, in descending order of importance:
+ * 1. whether the calling AE title is in the list of priority AEs
+ * 2. earliest sequence number
+ *
+ * This only happens after the store in Storage Plugins.
+ */
+ final class ImageElement implements Comparable {
+ private final URI uri;
+ private final String callingAET;
+ private final long seqNumber;
+
+ ImageElement(URI uri, String callingAET, long seqNumber) {
+ Objects.requireNonNull(uri);
+ Objects.requireNonNull(callingAET);
+ this.uri = uri;
+ this.callingAET = callingAET;
+ this.seqNumber = seqNumber;
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+
+ public String getCallingAET() {
+ return callingAET;
+ }
+
+ public long getSequenceNumber() {
+ return seqNumber;
+ }
+
+ @Override
+ public int compareTo(ImageElement other) {
+ return compareElementsImpl(DicomStorage.this.priorityAETs, this.callingAET, this.seqNumber,
+ other.callingAET, other.seqNumber);
+ }
+ }
+
+ /** Standalone implementation of image element sorting criteria.
+ *
+ * @param priorityAETs the set of calling AE titles
+ * from which incoming images have a HIGHER priority
+ * @param thisAETitle this image's AE title
+ * @param thisSeqNumber this image's sequence number
+ * @param otherAETitle the other image's AE title
+ * @param otherSeqNumber the other image's sequence number
+ * @return a number < 0 if this image has a higher priority than the other,
+ * > 0 if the other image has a higher priority
+ * (=0 should not happen because sequence numbers are unique)
+ * @see {@link ImageElement#compareTo(ImageElement)}
+ */
+ static int compareElementsImpl(Set priorityAETs, String thisAETitle, long thisSeqNumber,
+ String otherAETitle, long otherSeqNumber) {
+ boolean thisPriority = priorityAETs.contains(thisAETitle);
+ boolean thatPriority = priorityAETs.contains(otherAETitle);
+
+ int priorityOrder = Boolean.compare(thisPriority, thatPriority);
+
+ if (priorityOrder != 0) {
+ return -priorityOrder;
+ }
+
+ return Long.compare(thisSeqNumber, otherSeqNumber);
+ }
+
+ class Indexer extends Thread {
+ public Collection plugins;
+
+ public void run() {
+ while (true) {
+ try {
+ // Fetch an element by the queue taking into account the priorities.
+ ImageElement element = queue.take();
+ URI exam = element.getUri();
+ PluginController.getInstance().indexBlocking(exam);
+ } catch (InterruptedException ex) {
+ LoggerFactory.getLogger(DicomStorage.class).error("Could not take instance to index", ex);
+ }
+
+ }
+
+ }
+ }
+
+ private Indexer indexer = new Indexer();
+
+ /*
+ * Start the Storage Service
+ * @throws java.io.IOException
+ */
+ public void start() throws IOException {
+ device.startListening(executor);
+ indexer.start();
+ }
+
+ /**
+ * Stop the storage service
+ */
+ public void stop() {
+ device.stopListening();
+ }
+}
diff --git a/dicoogle/src/test/java/pt/ua/dicoogle/server/DicomStorageTest.java b/dicoogle/src/test/java/pt/ua/dicoogle/server/DicomStorageTest.java
new file mode 100644
index 000000000..e27b4f1b7
--- /dev/null
+++ b/dicoogle/src/test/java/pt/ua/dicoogle/server/DicomStorageTest.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
+ *
+ * This file is part of Dicoogle/dicoogle.
+ *
+ * Dicoogle/dicoogle is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dicoogle/dicoogle is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dicoogle. If not, see .
+ */
+
+package pt.ua.dicoogle.server;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Test;
+
+public class DicomStorageTest {
+ final Set priorityAETs = new HashSet<>(Arrays.asList("IMPORTANT AE 1", "IMPORTANT AE 2"));
+
+ private void assertPriorityOrderAsc(String aet1, long seq1, String aet2, long seq2) {
+ int order = DicomStorage.compareElementsImpl(priorityAETs, aet1, seq1, aet2, seq2);
+
+ assertTrue(String.format("Expected comparison (%s, %d)-(%s, %d) to be <0, but got %d", aet1, seq1, aet2, seq2,
+ order), order < 0);
+ }
+
+ @Test
+ public void testImageElementPriority() {
+
+ assertPriorityOrderAsc("AE01", 3, "AE02", 4);
+
+ assertPriorityOrderAsc("IMPORTANT AE 1", 0, "AE01", 200);
+
+ assertPriorityOrderAsc("IMPORTANT AE 1", 5000, "AE01", 2);
+
+ assertPriorityOrderAsc("IMPORTANT AE 1", 4, "IMPORTANT AE 2", 40);
+
+ }
+}