/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is part of dcm4che, an implementation of DICOM(TM) in * Java(TM), hosted at https://github.com/gunterze/dcm4che. * * The Initial Developer of the Original Code is * Agfa Healthcare. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * See @authors listed below * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.dcm4chee.archive.query.impl; import org.dcm4che3.data.Attributes; import org.dcm4che3.data.IDWithIssuer; import org.dcm4che3.io.SAXTransformer; import org.dcm4che3.io.SAXTransformer.SetupTransformer; import org.dcm4che3.net.*; import org.dcm4che3.net.service.DicomServiceException; import org.dcm4che3.util.DateUtils; import org.dcm4chee.archive.conf.ArchiveAEExtension; import org.dcm4chee.archive.conf.ArchiveDeviceExtension; import org.dcm4chee.archive.conf.QueryParam; import org.dcm4chee.archive.conf.QueryRetrieveView; import org.dcm4chee.archive.entity.Series; import org.dcm4chee.archive.entity.SeriesQueryAttributes; import org.dcm4chee.archive.entity.Study; import org.dcm4chee.archive.entity.StudyQueryAttributes; import org.dcm4chee.archive.query.Query; import org.dcm4chee.archive.query.QueryContext; import org.dcm4chee.archive.query.QueryService; import org.hibernate.Session; import org.hibernate.StatelessSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import java.util.Date; import java.util.EnumSet; /** * @author Gunter Zeilinger <gunterze@gmail.com> * @author Hesham Elbadawi <bsdreko@gmail.com> */ @ApplicationScoped public class DefaultQueryService implements QueryService { private static Logger LOG = LoggerFactory.getLogger(DefaultQueryService.class); @PersistenceContext(name = "dcm4chee-arc", unitName = "dcm4chee-arc") private EntityManager em; @Inject QueryServiceEJB ejb; @Inject Device device; StatelessSession openStatelessSession() { return em.unwrap(Session.class).getSessionFactory() .openStatelessSession(); } @Override public QueryContext createQueryContext(QueryService queryService) { return new QueryContextImpl(queryService); } @Override public Query createPatientQuery(QueryContext ctx) { return new PatientQuery(ctx, openStatelessSession()); } @Override public Query createStudyQuery(QueryContext ctx) { return new StudyQuery(ctx, openStatelessSession()); } @Override public Query createSeriesQuery(QueryContext ctx) { return new SeriesQuery(ctx, openStatelessSession()); } @Override public Query createInstanceQuery(QueryContext ctx) { return new InstanceQuery(ctx, openStatelessSession()); } @Override public Query createMWLItemQuery(QueryContext ctx) { // The MWL item query is currently not implemented and just always returns 0 results (DCMEEREQ-359) return new EmptyQuery(ctx); } @Override public Attributes getSeriesAttributes(Long seriesPk, QueryContext context) { return ejb.getSeriesAttributes(seriesPk, context); } @Override public QueryParam getQueryParam(Object source, String sourceAET, ArchiveAEExtension aeExt, EnumSet<QueryOption> queryOpts, String[] accessControlIDs) { return aeExt.getQueryParam(queryOpts, accessControlIDs); } @Override public void initPatientIDs(QueryContext ctx) { IDWithIssuer pid = IDWithIssuer.pidOf(ctx.getKeys()); ctx.setPatientIDs(pid == null ? IDWithIssuer.EMPTY : new IDWithIssuer[] { pid }); } /* * coerceAttributesForRequest applies a loaded XSL stylesheet on the keys * per request if given currently 17/4/2014 modifies date and time * attributes in the keys per request */ @Override public void coerceRequestAttributes(final QueryContext context) throws DicomServiceException { try { ArchiveAEExtension arcAE = context.getArchiveAEExtension(); Attributes keys = context.getKeys(); Templates tpl = arcAE.getAttributeCoercionTemplates( context.getServiceSOPClassUID(), Dimse.C_FIND_RQ, TransferCapability.Role.SCP, context.getRemoteAET()); if (tpl != null) { keys.addAll( SAXTransformer.transform(keys, tpl, false, false, new SetupTransformer() { @Override public void setup(Transformer transformer) { setParameters(transformer,context); } })); } } catch (Exception e) { throw new DicomServiceException(Status.UnableToProcess, e); } //time zone support moved to decorator } /* * coerceAttributesForResponse applies a loaded XSL stylesheet on the keys * per response if given currently 17/4/2014 modifies date and time * attributes in the keys per response */ @Override public void coerceResponseAttributes(final QueryContext context, Attributes match) throws DicomServiceException { try { ArchiveAEExtension arcAE = context.getArchiveAEExtension(); Attributes attrs = match; Templates tpl = arcAE.getAttributeCoercionTemplates( context.getServiceSOPClassUID(), Dimse.C_FIND_RSP, TransferCapability.Role.SCP, context.getRemoteAET()); if (tpl != null) { attrs.addAll(SAXTransformer.transform(attrs, tpl, false, false, new SetupTransformer() { @Override public void setup(Transformer transformer) { setParameters(transformer,context); } })); } } catch (Exception e) { throw new DicomServiceException(Status.UnableToProcess, e); } //time zone support moved to decorator } private void setParameters(Transformer transformer, QueryContext context) { Date date = new Date(); String currentDate = DateUtils.formatDA(null, date); String currentTime = DateUtils.formatTM(null, date); transformer.setParameter("date", currentDate); transformer.setParameter("time", currentTime); transformer.setParameter("calling", context.getRemoteAET()); transformer.setParameter("called", context.getArchiveAEExtension().getApplicationEntity().getAETitle()); } @Override public StudyQueryAttributes createStudyView(Long studyPk, QueryParam queryParam) { if (queryParam == null || queryParam.getQueryRetrieveView() == null) throw new IllegalArgumentException("Cannot create study view - queryParam/queryRetrieveView cannot be null"); return ejb.calculateStudyQueryAttributes(studyPk, queryParam); } @Override public SeriesQueryAttributes createSeriesView(Long seriesPk, QueryParam queryParam) { if (queryParam == null || queryParam.getQueryRetrieveView() == null) throw new IllegalArgumentException("Cannot create series view - queryParam/queryRetrieveView cannot be null"); return ejb.calculateSeriesQueryAttributes(seriesPk, queryParam); } @Override public void calculateDerivedFields(Study study, ApplicationEntity ae) { LOG.info("Calculating derived fields"); ArchiveDeviceExtension arcDevExt = device.getDeviceExtension(ArchiveDeviceExtension.class); ArchiveAEExtension arcAEExt = ae.getAEExtension(ArchiveAEExtension.class); QueryRetrieveView view = arcDevExt.getQueryRetrieveView(arcAEExt.getQueryRetrieveViewID()); if (view == null) { LOG.warn("Cannot re-calculate derived fields - query retrieve view ID is not specified for AE {}", ae.getAETitle()); return; } QueryParam param = new QueryParam(); param.setQueryRetrieveView(view); try { //create study view createStudyView(study.getPk(), param); //create series view for (Series series : study.getSeries()) createSeriesView(series.getPk(), param); } catch (Exception e) { LOG.error("Error while calculating derived fields on MPPS COMPLETE", e); } } }