diff --git a/.github/workflows/build-2.x.yaml b/.github/workflows/build-2.x.yaml index c6164fbefc7e..3b507728f4b2 100644 --- a/.github/workflows/build-2.x.yaml +++ b/.github/workflows/build-2.x.yaml @@ -28,14 +28,14 @@ jobs: - 8 runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'adopt' java-version: ${{ matrix.java-version }} - name: Cache Maven packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b139ab896fda..883d0f20309c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -8,12 +8,14 @@ on: - master - 2.4.x - 2.5.x + - 2.6.x pull_request: types: branches: - master - 2.4.x - 2.5.x + - 2.6.x workflow_dispatch: jobs: @@ -25,30 +27,20 @@ jobs: java-version: - 8 - 11 + - 17 runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'adopt' + distribution: 'temurin' java-version: ${{ matrix.java-version }} - - name: Cache Maven packages - uses: actions/cache@v3 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - name: Cache SonarCloud packages - uses: actions/cache@v3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar + cache: 'maven' - name: Install dependencies run: mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true --batch-mode --show-version --file pom.xml - name: Build with Maven - run: mvn clean install && mvn test -Pskip-default-test -Pintegration-test --batch-mode --file pom.xml + run: mvn clean install --batch-mode && mvn test -Pskip-default-test -Pintegration-test --batch-mode --file pom.xml # this is necessary to populate the environment variables for Coveralls properly - name: Set branch name and PR number id: refs diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ccfab002256e..23cbfb9bf8eb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -67,4 +67,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml deleted file mode 100644 index f486ebb703e6..000000000000 --- a/.github/workflows/qa.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: QaFramework Trigger -on: - push: - branches: [master] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Trigger QAFramework - uses: peter-evans/repository-dispatch@v2 - with: - token: ${{secrets.ACTIONS_TOKEN}} - repository: openmrs/openmrs-contrib-qaframework - event-type: qa diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index e1666d19edb7..b7cb7eaf2d97 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v7 + - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 150 diff --git a/.gitignore b/.gitignore index f1992bf4bb70..a77727e8d7f3 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,7 @@ nb-configuration.xml /webapp/src/main/webapp/WEB-INF/dwr-modules.xml /webapp/src/main/webapp/WEB-INF/module_messages* /web/WEB-INF/dwr-modules.xml +/openmrs-merged.properties ############# ### log files ### diff --git a/Dockerfile b/Dockerfile index 6e9ef1837c54..08955eee9daf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,10 +8,47 @@ # Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS # graphic logo is a trademark of OpenMRS Inc. +### Compile Stage (platform-agnostic) +FROM --platform=$BUILDPLATFORM maven:3.8-amazoncorretto-8 as compile + +RUN yum -y update && yum -y install git && yum clean all + +WORKDIR /openmrs_core + +ENV OMRS_SDK_PLUGIN="org.openmrs.maven.plugins:openmrs-sdk-maven-plugin" +ENV OMRS_SDK_PLUGIN_VERSION="4.5.0" + +COPY docker-pom.xml . + +ARG MVN_SETTINGS="-s /usr/share/maven/ref/settings-docker.xml" + +# Setup and cache SDK +RUN mvn $MVN_SETTINGS -f docker-pom.xml $OMRS_SDK_PLUGIN:$OMRS_SDK_PLUGIN_VERSION:setup-sdk -N -DbatchAnswers=n + +COPY pom.xml . +COPY test/pom.xml test/ +COPY tools/pom.xml tools/ +COPY liquibase/pom.xml liquibase/ +COPY api/pom.xml api/ +COPY web/pom.xml web/ +COPY webapp/pom.xml webapp/ + +# Install dependencies +RUN mvn $MVN_SETTINGS -B dependency:go-offline -P !default-tools.jar,!mac-tools.jar + +# Copy remaining files +COPY . . + +# Append --build-arg MVN_ARGS='clean install' to change default maven arguments +ARG MVN_ARGS='clean install -DskipTests' + +# Build the project +RUN mvn $MVN_SETTINGS $MVN_ARGS + ### Development Stage FROM maven:3.8-amazoncorretto-8 as dev -RUN yum -y update && yum -y install tar gzip && yum clean all +RUN yum -y update && yum -y install tar gzip git && yum clean all # Setup Tini ARG TARGETARCH @@ -30,70 +67,20 @@ ARG TOMCAT_SHA="57cbe9608a9c4e88135e5f5480812e8d57690d5f3f6c43a7c05fe647bddb7c3b ARG TOMCAT_URL="https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-8/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" RUN curl -fL -o /tmp/apache-tomcat.tar.gz "$TOMCAT_URL" \ && echo "${TOMCAT_SHA} /tmp/apache-tomcat.tar.gz" | sha512sum -c \ - && mkdir -p /usr/local/tomcat && gzip -d /tmp/apache-tomcat.tar.gz && tar -xvf /tmp/apache-tomcat.tar -C /usr/local/tomcat/ --strip-components=1 \ - && rm -rf /tmp/apache-tomcat.tar.gz /usr/local/tomcat/webapps/* + && mkdir -p /usr/local/tomcat && gzip -d /tmp/apache-tomcat.tar.gz \ + && tar -xvf /tmp/apache-tomcat.tar -C /usr/local/tomcat/ --strip-components=1 \ + && rm -rf /tmp/apache-tomcat.tar.gz /usr/local/tomcat/webapps/* WORKDIR /openmrs_core -ENV OPENMRS_SDK_PLUGIN="org.openmrs.maven.plugins:openmrs-sdk-maven-plugin:4.5.0" -ENV OPENMRS_SDK_PLUGIN_VERSION="4.5.0" -ENV MVN_ARGS_SETTINGS="-s /usr/share/maven/ref/settings-docker.xml" +COPY --from=compile /usr/share/maven/ref /usr/share/maven/ref -COPY checkstyle.xml checkstyle-suppressions.xml CONTRIBUTING.md findbugs-include.xml LICENSE license-header.txt \ - NOTICE.md README.md ruleset.xml SECURITY.md ./ - -COPY pom.xml . - -# Setup and cache SDK -RUN mvn $OPENMRS_SDK_PLUGIN:setup-sdk -N -DbatchAnswers=n $MVN_ARGS_SETTINGS - -# Store dependencies in /usr/share/maven/ref/repository for re-use when running -# If mounting ~/.m2:/root/.m2 then the /usr/share/maven/ref content will be copied over from the image to /root/.m2 -RUN mvn --non-recursive dependency:go-offline $MVN_ARGS_SETTINGS - -# Copy remainig poms to satisfy dependencies -COPY liquibase/pom.xml ./liquibase/ -COPY tools/pom.xml ./tools/ -COPY test/pom.xml ./test/ -COPY api/pom.xml ./api/ -COPY web/pom.xml ./web/ -COPY webapp/pom.xml ./webapp/ - -# Exclude tools as it fails trying to fetch tools.jar -RUN mvn -pl !tools dependency:go-offline $MVN_ARGS_SETTINGS - -# Append --build-arg MVN_ARGS='install' to change default maven arguments -# Build modules individually to benefit from caching -ARG MVN_ARGS='install' - -# Build the parent project -RUN mvn --non-recursive $MVN_ARGS_SETTINGS $MVN_ARGS - -# Build individually to benefit from caching -COPY liquibase ./liquibase/ -RUN mvn -pl liquibase $MVN_ARGS_SETTINGS $MVN_ARGS - -COPY tools/ ./tools/ -RUN mvn -pl tools $MVN_ARGS_SETTINGS $MVN_ARGS - -COPY test/ ./test/ -RUN mvn -pl test $MVN_ARGS_SETTINGS $MVN_ARGS - -COPY api/ ./api/ -RUN mvn -pl api $MVN_ARGS_SETTINGS $MVN_ARGS - -COPY web/ ./web/ -RUN mvn -pl web $MVN_ARGS_SETTINGS $MVN_ARGS - -COPY webapp/ ./webapp/ -RUN mvn -pl webapp $MVN_ARGS_SETTINGS $MVN_ARGS +COPY --from=compile /openmrs_core /openmrs_core/ RUN mkdir -p /openmrs/distribution/openmrs_core/ \ - && cp /openmrs_core/webapp/target/openmrs.war /openmrs/distribution/openmrs_core/openmrs.war - -# Copy in the start-up scripts -COPY wait-for-it.sh startup-init.sh startup.sh startup-dev.sh /openmrs/ -RUN chmod +x /openmrs/wait-for-it.sh && chmod +x /openmrs/startup-init.sh && chmod +x /openmrs/startup.sh \ + && cp /openmrs_core/webapp/target/openmrs.war /openmrs/distribution/openmrs_core/openmrs.war \ + && cp /openmrs_core/wait-for-it.sh /openmrs_core/startup-init.sh /openmrs_core/startup.sh /openmrs_core/startup-dev.sh /openmrs/ \ + && chmod +x /openmrs/wait-for-it.sh && chmod +x /openmrs/startup-init.sh && chmod +x /openmrs/startup.sh \ && chmod +x /openmrs/startup-dev.sh EXPOSE 8080 @@ -107,7 +94,7 @@ CMD ["/openmrs/startup-dev.sh"] ### Production Stage FROM tomcat:8.5-jdk8-corretto -RUN yum -y update && yum -y install shadow-utils && yum clean all && rm -rf /usr/local/tomcat/webapps/* +RUN yum -y update && yum clean all && rm -rf /usr/local/tomcat/webapps/* # Setup Tini ARG TARGETARCH @@ -118,19 +105,23 @@ ARG TINI_SHA_ARM64="07952557df20bfd2a95f9bef198b445e006171969499a1d361bd9e6f8e5e RUN if [ "$TARGETARCH" = "arm64" ] ; then TINI_URL="${TINI_URL}-arm64" TINI_SHA=${TINI_SHA_ARM64} ; fi \ && curl -fsSL -o /usr/bin/tini ${TINI_URL} \ && echo "${TINI_SHA} /usr/bin/tini" | sha256sum -c \ - && chmod +x /usr/bin/tini + && chmod g+rx /usr/bin/tini RUN sed -i '/Connector port="8080"/a URIEncoding="UTF-8" relaxedPathChars="[]|" relaxedQueryChars="[]|{}^\`"<>"' \ - /usr/local/tomcat/conf/server.xml + /usr/local/tomcat/conf/server.xml \ + && chmod -R g+rx /usr/local/tomcat \ + && touch /usr/local/tomcat/bin/setenv.sh && chmod g+w /usr/local/tomcat/bin/setenv.sh \ + && chmod -R g+w /usr/local/tomcat/webapps /usr/local/tomcat/logs /usr/local/tomcat/work /usr/local/tomcat/temp -RUN adduser openmrs && mkdir -p /openmrs/data/modules \ +RUN mkdir -p /openmrs/data/modules \ && mkdir -p /openmrs/data/owa \ && mkdir -p /openmrs/data/configuration \ - && chown -R openmrs /openmrs + && mkdir -p /openmrs/data/configuration_checksums \ + && chmod -R g+rw /openmrs # Copy in the start-up scripts -COPY wait-for-it.sh startup-init.sh startup.sh /openmrs/ -RUN chmod +x /openmrs/wait-for-it.sh && chmod +x /openmrs/startup-init.sh && chmod +x /openmrs/startup.sh +COPY --from=dev /openmrs/wait-for-it.sh /openmrs/startup-init.sh /openmrs/startup.sh /openmrs/ +RUN chmod g+x /openmrs/wait-for-it.sh && chmod g+x /openmrs/startup-init.sh && chmod g+x /openmrs/startup.sh WORKDIR /openmrs @@ -140,6 +131,10 @@ COPY --from=dev /openmrs/distribution/openmrs_core/openmrs.war /openmrs/distribu EXPOSE 8080 +# Run as non-root user using Bitnami approach, see e.g. +# https://github.com/bitnami/containers/blob/6c8f10bbcf192ab4e575614491abf10697c46a3e/bitnami/tomcat/8.5/debian-11/Dockerfile#L54 +USER 1001 + ENTRYPOINT ["/usr/bin/tini", "--"] # See startup-init.sh for all configurable environment variables diff --git a/README.md b/README.md index 0994cd58e7f2..a5eb08a94bb2 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ docker-compose build ``` It calls `mvn install` by default. If you would like to customize mvn build arguments you can do so by running: ```bash -docker-compose build --build-args MVN_ARGS='install -DskipTests' +docker-compose build --build-arg MVN_ARGS='install -DskipTests' ``` It is also possible to use the built dev image to run jetty: ```bash @@ -124,6 +124,28 @@ The production version can be run with: ```bash docker-compose -f docker-compose.yml up ``` +If you want to debug, you need to run a development version and connect your debugger to port 8000, which is exposed by default. + +Unfortunately, at this point any code changes require full restart and rebuild of the docker container. To speed up the process, +please use: +```bash +docker-compose build --build-arg MVN_ARGS='install -DskipTests' +docker-compose up +``` +We are working towards providing support for Spring Boot auto-reload feature, which will be documented here once ready. + +It is also possible to deploy an image built by our CI, which is published at +https://hub.docker.com/r/openmrs/openmrs-core + +You can run any tag available with: +```bash +TAG=nightly docker-compose -f docker-compose.yml up +``` +It is also possible to run a development version of an image with: +```bash +TAG=dev docker-compose up +``` +All development versions contain dev suffix. The cache suffix is for use by our CI. ## Navigating the repository diff --git a/api/pom.xml b/api/pom.xml index ba4741d36910..d92c3468cc44 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -70,10 +70,6 @@ commons-io commons-io - - org.azeckoski - reflectutils - org.apache.velocity velocity @@ -279,6 +275,10 @@ org.apache.lucene lucene-analyzers-phonetic + + com.google.guava + guava + com.sun.mail javax.mail @@ -292,6 +292,10 @@ org.testcontainers mysql + + org.junit.jupiter + junit-jupiter-params + diff --git a/api/src/main/java/org/openmrs/BaseCustomizableMetadata.java b/api/src/main/java/org/openmrs/BaseCustomizableMetadata.java index b18f445ab2c4..1b3d3e0f23ba 100644 --- a/api/src/main/java/org/openmrs/BaseCustomizableMetadata.java +++ b/api/src/main/java/org/openmrs/BaseCustomizableMetadata.java @@ -9,23 +9,35 @@ */ package org.openmrs; +import org.hibernate.annotations.BatchSize; +import org.openmrs.attribute.Attribute; +import org.openmrs.customdatatype.CustomValueDescriptor; +import org.openmrs.customdatatype.Customizable; + +import javax.persistence.CascadeType; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.MappedSuperclass; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import org.openmrs.attribute.Attribute; -import org.openmrs.customdatatype.CustomValueDescriptor; -import org.openmrs.customdatatype.Customizable; - /** * Extension of {@link org.openmrs.BaseOpenmrsMetadata} for classes that support customization via user-defined attributes. * @param the type of attribute held * @since 1.9 */ +@MappedSuperclass public abstract class BaseCustomizableMetadata extends BaseChangeableOpenmrsMetadata implements Customizable { + @OrderBy("voided asc") + @BatchSize(size = 100) + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn(name = "location_id") private Set attributes = new LinkedHashSet<>(); /** diff --git a/api/src/main/java/org/openmrs/ConceptName.java b/api/src/main/java/org/openmrs/ConceptName.java index 5691ad040a5b..8816525a75cb 100644 --- a/api/src/main/java/org/openmrs/ConceptName.java +++ b/api/src/main/java/org/openmrs/ConceptName.java @@ -16,6 +16,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.lucene.analysis.core.LowerCaseFilterFactory; +import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilterFactory; import org.apache.lucene.analysis.standard.StandardFilterFactory; import org.apache.lucene.analysis.standard.StandardTokenizerFactory; import org.codehaus.jackson.annotate.JsonIgnore; @@ -37,8 +38,12 @@ * locale. */ @Indexed -@AnalyzerDef(name = "ConceptNameAnalyzer", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { - @TokenFilterDef(factory = StandardFilterFactory.class), @TokenFilterDef(factory = LowerCaseFilterFactory.class) }) +@AnalyzerDef( + name = "ConceptNameAnalyzer", tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), filters = { + @TokenFilterDef(factory = StandardFilterFactory.class), + @TokenFilterDef(factory = LowerCaseFilterFactory.class), + @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class) + }) @Analyzer(definition = "ConceptNameAnalyzer") public class ConceptName extends BaseOpenmrsObject implements Auditable, Voidable, java.io.Serializable { diff --git a/api/src/main/java/org/openmrs/Condition.java b/api/src/main/java/org/openmrs/Condition.java index 93fd2a81cac0..c16e86f6c1f4 100644 --- a/api/src/main/java/org/openmrs/Condition.java +++ b/api/src/main/java/org/openmrs/Condition.java @@ -9,6 +9,8 @@ */ package org.openmrs; +import org.openmrs.util.OpenmrsUtil; + import javax.persistence.AssociationOverride; import javax.persistence.AssociationOverrides; import javax.persistence.AttributeOverride; @@ -25,7 +27,6 @@ import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Transient; - import java.util.Date; /** @@ -113,26 +114,79 @@ public Condition(CodedOrFreeText condition, ConditionClinicalStatus clinicalStat this.endDate = endDate != null ? new Date(endDate.getTime()) : null; this.patient = patient; } - + + /** + * Creates a new Condition instance from the passed condition such that the newly created Condition + * matches the passed Condition @see Condition#matches, but does not equal the passed Condition (uuid, id differ) + * @param condition the Condition to copy + * @return a new Condition that is a copy of the passed condition + */ public static Condition newInstance(Condition condition) { return copy(condition, new Condition()); } - + + /** + * Copies property values from the fromCondition to the toCondition such that fromCondition + * matches toCondition @see Condition#matches, but does not equal toCondition (uuid, id differ) + * @param fromCondition the Condition to copy from + * @param toCondition the Condition to copy into + * @return a new Condition that is a copy of the passed condition + */ public static Condition copy(Condition fromCondition, Condition toCondition) { toCondition.setPreviousVersion(fromCondition.getPreviousVersion()); toCondition.setPatient(fromCondition.getPatient()); + toCondition.setEncounter(fromCondition.getEncounter()); + toCondition.setFormNamespaceAndPath(fromCondition.getFormNamespaceAndPath()); toCondition.setClinicalStatus(fromCondition.getClinicalStatus()); toCondition.setVerificationStatus(fromCondition.getVerificationStatus()); toCondition.setCondition(fromCondition.getCondition()); toCondition.setOnsetDate(fromCondition.getOnsetDate()); toCondition.setAdditionalDetail(fromCondition.getAdditionalDetail()); toCondition.setEndDate(fromCondition.getEndDate()); + toCondition.setEndReason(fromCondition.getEndReason()); toCondition.setVoided(fromCondition.getVoided()); toCondition.setVoidedBy(fromCondition.getVoidedBy()); toCondition.setVoidReason(fromCondition.getVoidReason()); toCondition.setDateVoided(fromCondition.getDateVoided()); return toCondition; } + + /** + * Compares properties with those in the given Condition to determine if they have the same meaning + * This method will return true immediately following the creation of a Condition from another Condition + * @see Condition#newInstance(Condition) + * This method will return false if any value is different, excepting identity data (id, uuid) + * If the given instance is null, this will return false + * @param c the Condition to compare against + * @return true if the given Condition has the same meaningful properties as the passed Condition + * @since 2.6.1 + */ + public boolean matches(Condition c) { + if (c == null) { + return false; + } + CodedOrFreeText coft1 = getCondition() == null ? new CodedOrFreeText() : getCondition(); + CodedOrFreeText coft2 = c.getCondition() == null ? new CodedOrFreeText() : c.getCondition(); + + boolean ret = (OpenmrsUtil.nullSafeEquals(getPreviousVersion(), c.getPreviousVersion())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getPatient(), c.getPatient())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getEncounter(), c.getEncounter())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getFormNamespaceAndPath(), c.getFormNamespaceAndPath())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getClinicalStatus(), c.getClinicalStatus())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getVerificationStatus(), c.getVerificationStatus())); + ret = ret && (OpenmrsUtil.nullSafeEquals(coft1.getCoded(), coft2.getCoded())); + ret = ret && (OpenmrsUtil.nullSafeEquals(coft1.getSpecificName(), coft2.getSpecificName())); + ret = ret && (OpenmrsUtil.nullSafeEquals(coft1.getNonCoded(), coft2.getNonCoded())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getOnsetDate(), c.getOnsetDate())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getAdditionalDetail(), c.getAdditionalDetail())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getEndDate(), c.getEndDate())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getEndReason(), c.getEndReason())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getVoided(), c.getVoided())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getVoidedBy(), c.getVoidedBy())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getVoidReason(), c.getVoidReason())); + ret = ret && (OpenmrsUtil.nullSafeEquals(getDateVoided(), c.getDateVoided())); + return ret; + } /** * Gets the condition id diff --git a/api/src/main/java/org/openmrs/Encounter.java b/api/src/main/java/org/openmrs/Encounter.java index a11a09d83756..9b5daced688f 100644 --- a/api/src/main/java/org/openmrs/Encounter.java +++ b/api/src/main/java/org/openmrs/Encounter.java @@ -480,6 +480,9 @@ public void setPatient(Patient patient) { * @since 2.2 */ public Set getDiagnoses() { + if (diagnoses == null) { + diagnoses = new LinkedHashSet<>(); + } return diagnoses; } diff --git a/api/src/main/java/org/openmrs/GlobalProperty.java b/api/src/main/java/org/openmrs/GlobalProperty.java index 8201588e50d7..acc8ccae05ce 100644 --- a/api/src/main/java/org/openmrs/GlobalProperty.java +++ b/api/src/main/java/org/openmrs/GlobalProperty.java @@ -48,6 +48,13 @@ public class GlobalProperty extends BaseOpenmrsObject implements CustomValueDesc private Date dateChanged; + private Privilege viewPrivilege; + + private Privilege editPrivilege; + + private Privilege deletePrivilege; + + /** * Default empty constructor */ @@ -337,4 +344,64 @@ public Date getDateChanged() { public void setDateChanged(Date dateChanged) { this.dateChanged = dateChanged; } + + /** + * Gets privilege which can view this globalProperty + * @return the viewPrivilege the privilege instance + * + * @since 2.7.0 + */ + public Privilege getViewPrivilege() { + return viewPrivilege; + } + + /** + * Sets privilege which can view this globalProperty + * @param viewPrivilege the viewPrivilege to set + * + * @since 2.7.0 + */ + public void setViewPrivilege(Privilege viewPrivilege) { + this.viewPrivilege = viewPrivilege; + } + + /** + * Gets privilege which can edit this globalProperty + * @return the editPrivilege the privilege instance + * + * @since 2.7.0 + */ + public Privilege getEditPrivilege() { + return editPrivilege; + } + + /** + * Sets privilege which can edit this globalProperty + * @param editPrivilege the editPrivilege to set + * + * @since 2.7.0 + */ + public void setEditPrivilege(Privilege editPrivilege) { + this.editPrivilege = editPrivilege; + } + + /** + * Get privilege which can delete this globalProperty + * @return the deletePrivilege the privilege instance + * + * @since 2.7.0 + */ + public Privilege getDeletePrivilege() { + return deletePrivilege; + } + + /** + * Sets privilege which can delete this globalProperty + * @param deletePrivilege the deletePrivilege to set + * + * @since 2.7.0 + */ + public void setDeletePrivilege(Privilege deletePrivilege) { + this.deletePrivilege = deletePrivilege; + } } diff --git a/api/src/main/java/org/openmrs/Location.java b/api/src/main/java/org/openmrs/Location.java index d465a6ccb729..329f063a8c75 100644 --- a/api/src/main/java/org/openmrs/Location.java +++ b/api/src/main/java/org/openmrs/Location.java @@ -9,15 +9,33 @@ */ package org.openmrs; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.openmrs.annotation.Independent; +import org.openmrs.api.APIException; +import org.openmrs.api.context.Context; + +import javax.persistence.AttributeOverride; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; +import javax.persistence.Table; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.openmrs.annotation.Independent; -import org.openmrs.api.APIException; -import org.openmrs.api.context.Context; - /** * A Location is a physical place, such as a hospital, a room, a clinic, or a district. Locations * support a single hierarchy, such that each location may have one parent location. A @@ -25,6 +43,10 @@ * and should be modeled using {@link LocationTag}s. * Note: Prior to version 1.9 this class extended BaseMetadata */ +@Entity +@Table(name = "location") +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +@AttributeOverride(name = "attributes", column = @Column(name = "location_id")) public class Location extends BaseCustomizableMetadata implements java.io.Serializable, Attributable, Address { public static final long serialVersionUID = 455634L; @@ -32,59 +54,95 @@ public class Location extends BaseCustomizableMetadata implem public static final int LOCATION_UNKNOWN = 1; // Fields - + @Id + @Column(name = "location_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer locationId; - + + @ManyToOne + @JoinColumn(name = "location_type_concept_id") private Concept type; + @Column(name = "address1") private String address1; + @Column(name = "address2") private String address2; + @Column(name = "city_village") private String cityVillage; + @Column(name = "state_province") private String stateProvince; + @Column(name = "country", length = 50) private String country; + @Column(name = "postal_code", length = 50) private String postalCode; + @Column(name = "latitude", length = 50) private String latitude; + @Column(name = "longitude", length = 50) private String longitude; + @Column(name = "county_district") private String countyDistrict; + @Column(name = "address3") private String address3; + @Column(name = "address4") private String address4; + @Column(name = "address6") private String address6; + @Column(name = "address5") private String address5; + @Column(name = "address7") private String address7; + @Column(name = "address8") private String address8; + @Column(name = "address9") private String address9; + @Column(name = "address10") private String address10; + @Column(name = "address11") private String address11; + @Column(name = "address12") private String address12; + @Column(name = "address13") private String address13; + @Column(name = "address14") private String address14; - + + @Column(name = "address15") private String address15; + @ManyToOne + @JoinColumn(name = "parent_location") private Location parentLocation; + @OneToMany(mappedBy = "parentLocation", cascade = CascadeType.ALL, orphanRemoval = true) + @BatchSize(size = 100) + @OrderBy("name") private Set childLocations; + @ManyToMany(fetch = FetchType.LAZY) + @JoinTable( + name = "location_tag_map", + joinColumns = @JoinColumn(name = "location_id"), + inverseJoinColumns = @JoinColumn(name = "location_tag_id")) @Independent private Set tags; diff --git a/api/src/main/java/org/openmrs/LocationAttributeType.java b/api/src/main/java/org/openmrs/LocationAttributeType.java index bd67e3581fe5..c9f1e67225bb 100644 --- a/api/src/main/java/org/openmrs/LocationAttributeType.java +++ b/api/src/main/java/org/openmrs/LocationAttributeType.java @@ -9,16 +9,35 @@ */ package org.openmrs; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Parameter; import org.openmrs.attribute.AttributeType; import org.openmrs.attribute.BaseAttributeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + /** * A user-defined extension to the {@link Location} class. * @see AttributeType * @since 1.9 */ +@Entity +@Table(name = "location_attribute_type") public class LocationAttributeType extends BaseAttributeType implements AttributeType { - + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "location_attribute_type_id_seq") + @GenericGenerator( + name = "location_attribute_type_id_seq", + strategy = "native", + parameters = @Parameter(name = "sequence", value = "location_attribute_type_location_attribute_type_id_seq") + ) + @Column(name = "location_attribute_type_id") private Integer locationAttributeTypeId; /** diff --git a/api/src/main/java/org/openmrs/Order.java b/api/src/main/java/org/openmrs/Order.java index bb0b542b1854..e9157dc3aeca 100644 --- a/api/src/main/java/org/openmrs/Order.java +++ b/api/src/main/java/org/openmrs/Order.java @@ -58,11 +58,14 @@ public enum Action { /** * Valid values for the status of an order that is received from a filler * @since 2.2.0 + * @since 2.6.1 added ON_HOLD & DECLINED */ public enum FulfillerStatus { - RECEIVED, + RECEIVED, IN_PROGRESS, EXCEPTION, + ON_HOLD, + DECLINED, COMPLETED } diff --git a/api/src/main/java/org/openmrs/PatientState.java b/api/src/main/java/org/openmrs/PatientState.java index d12b4ae45aeb..9166d2b78175 100644 --- a/api/src/main/java/org/openmrs/PatientState.java +++ b/api/src/main/java/org/openmrs/PatientState.java @@ -11,11 +11,24 @@ import java.util.Date; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Parameter; import org.openmrs.util.OpenmrsUtil; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + /** * PatientState */ +@Entity +@Table(name = "patient_state") public class PatientState extends BaseFormRecordableOpenmrsData implements java.io.Serializable, Comparable { public static final long serialVersionUID = 0L; @@ -23,17 +36,33 @@ public class PatientState extends BaseFormRecordableOpenmrsData implements java. // ****************** // Properties // ****************** - + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "patient_state_id_seq") + @GenericGenerator( + name = "patient_state_id_seq", + strategy = "native", + parameters = @Parameter(name = "sequence", value = "patient_state_patient_state_id_seq") + ) + @Column(name = "patient_state_id") private Integer patientStateId; - + + @ManyToOne + @JoinColumn(name = "patient_program_id", nullable = false) private PatientProgram patientProgram; - + + @ManyToOne + @JoinColumn(name = "state", nullable = false) private ProgramWorkflowState state; - + + @Column(name = "start_date", length = 19) private Date startDate; - + + @Column(name = "end_date", length = 19) private Date endDate; - + + @ManyToOne + @JoinColumn(name = "encounter_id") private Encounter encounter; // ****************** diff --git a/api/src/main/java/org/openmrs/PersonAddress.java b/api/src/main/java/org/openmrs/PersonAddress.java index 6c78767d25c8..17c5da255c83 100644 --- a/api/src/main/java/org/openmrs/PersonAddress.java +++ b/api/src/main/java/org/openmrs/PersonAddress.java @@ -9,78 +9,117 @@ */ package org.openmrs; -import static org.apache.commons.lang3.StringUtils.defaultString; - -import java.util.Calendar; -import java.util.Date; - import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.codehaus.jackson.annotate.JsonIgnore; import org.openmrs.util.OpenmrsUtil; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import java.util.Calendar; +import java.util.Date; + +import static org.apache.commons.lang3.StringUtils.defaultString; + /** * This class is the representation of a person's address. This class is many-to-one to the Person * class, so a Person/Patient/User can have zero to n addresses */ +@Entity +@Table(name = "person_address") public class PersonAddress extends BaseChangeableOpenmrsData implements java.io.Serializable, Cloneable, Comparable, Address { public static final long serialVersionUID = 343333L; // Fields - + @Id + @Column(name = "person_address_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer personAddressId; - + + @ManyToOne + @JoinColumn(name = "person_id") private Person person; + @Column(name = "preferred", length = 1, nullable = false) private Boolean preferred = false; - + + @Column(name = "address1") private String address1; - + + @Column(name = "address2") private String address2; - + + @Column(name = "address3") private String address3; - + + @Column(name = "address4") private String address4; - + + @Column(name = "address5") private String address5; - + + @Column(name = "address6") private String address6; - + + @Column(name = "address7") private String address7; - + + @Column(name = "address8") private String address8; - + + @Column(name = "address9") private String address9; - + + @Column(name = "address10") private String address10; - + + @Column(name = "address11") private String address11; - + + @Column(name = "address12") private String address12; - + + @Column(name = "address13") private String address13; - + + @Column(name = "address14") private String address14; - + + @Column(name = "address15") private String address15; + @Column(name = "city_village") private String cityVillage; + @Column(name = "county_district") private String countyDistrict; + @Column(name = "state_province") private String stateProvince; + @Column(name = "country") private String country; + @Column(name = "postal_code", length = 50) private String postalCode; + @Column(name = "latitude", length = 50) private String latitude; - + + @Column(name = "longitude", length = 50) private String longitude; + @Column(name = "start_date", length = 19) private Date startDate; + @Column(name = "end_date", length = 19) private Date endDate; // Constructors diff --git a/api/src/main/java/org/openmrs/PersonAttributeType.java b/api/src/main/java/org/openmrs/PersonAttributeType.java index d2d490e45a97..751dfe991c26 100644 --- a/api/src/main/java/org/openmrs/PersonAttributeType.java +++ b/api/src/main/java/org/openmrs/PersonAttributeType.java @@ -9,31 +9,50 @@ */ package org.openmrs; -import java.io.Serializable; -import java.util.Comparator; - import org.codehaus.jackson.annotate.JsonIgnore; import org.hibernate.search.annotations.Field; import org.openmrs.util.OpenmrsUtil; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Comparator; + /** * PersonAttributeType */ +@Entity +@Table(name = "person_attribute_type") public class PersonAttributeType extends BaseChangeableOpenmrsMetadata implements java.io.Serializable, Comparable { public static final long serialVersionUID = 2112313431211L; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "person_attribute_type_id") private Integer personAttributeTypeId; + @Column(name = "format", length = 50) private String format; + @Column(name = "foreign_key") private Integer foreignKey; + @Column(name = "sort_weight", nullable = false) private Double sortWeight; @Field + @Column(name = "searchable", nullable = false) private Boolean searchable = false; + @ManyToOne + @JoinColumn(name = "edit_privilege") private Privilege editPrivilege; /** default constructor */ diff --git a/api/src/main/java/org/openmrs/Relationship.java b/api/src/main/java/org/openmrs/Relationship.java index cd6e2f7355fc..59c840693ec1 100644 --- a/api/src/main/java/org/openmrs/Relationship.java +++ b/api/src/main/java/org/openmrs/Relationship.java @@ -9,27 +9,53 @@ */ package org.openmrs; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Parameter; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; import java.util.Date; /** * Relationship */ +@Entity +@Table(name = "relationship") public class Relationship extends BaseChangeableOpenmrsData { public static final long serialVersionUID = 323423L; // Fields - + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "relationship_id_seq") + @GenericGenerator( + name = "relationship_id_seq", + strategy = "native", + parameters = @Parameter(name = "sequence", value = "relationship_relationship_id_seq") + ) + @Column(name = "relationship_id") private Integer relationshipId; - + @ManyToOne(optional = false) + @JoinColumn(name = "person_a", nullable = false) private Person personA; - + + @ManyToOne(optional = false) + @JoinColumn(name = "relationship", nullable = false) private RelationshipType relationshipType; - + + @ManyToOne(optional = false) + @JoinColumn(name = "person_b", nullable = false) private Person personB; - + + @Column(name = "start_date",length = 19) private Date startDate; - + @Column(name = "end_date", length = 19) private Date endDate; // Constructors diff --git a/api/src/main/java/org/openmrs/User.java b/api/src/main/java/org/openmrs/User.java index dcd69ac55d45..77c51355c50e 100644 --- a/api/src/main/java/org/openmrs/User.java +++ b/api/src/main/java/org/openmrs/User.java @@ -9,6 +9,20 @@ */ package org.openmrs; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import javax.persistence.Transient; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -21,6 +35,14 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; +import org.hibernate.annotations.Parameter; import org.openmrs.api.context.Context; import org.openmrs.util.LocaleUtility; import org.openmrs.util.OpenmrsConstants; @@ -36,6 +58,10 @@ * key-value pairs for either quick info or display specific info that needs to be persisted (like * locale preferences, search options, etc) */ + +@Entity +@Table(name = "users") +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class User extends BaseOpenmrsObject implements java.io.Serializable, Attributable, Auditable, Retireable { public static final long serialVersionUID = 2L ; @@ -43,38 +69,76 @@ public class User extends BaseOpenmrsObject implements java.io.Serializable, Att private static final Logger log = LoggerFactory.getLogger(User.class); // Fields + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "users_user_id_seq") + @GenericGenerator( + name = "users_user_id_seq", + strategy = "native", + parameters = @Parameter(name = "sequence", value = "users_user_id_seq") + ) + @Column(name = "user_id") private Integer userId; - + + @ManyToOne + @JoinColumn(name = "person_id", nullable = false) + @LazyCollection(LazyCollectionOption.FALSE) + @Cascade(CascadeType.SAVE_UPDATE) private Person person; - + + @Column(name = "system_id", nullable = false, length = 50) private String systemId; - + + @Column(name = "username", length = 50) private String username; - + + @Column(name = "email", length = 255, unique = true) private String email; - + + @ManyToMany + @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role")) + @LazyCollection(LazyCollectionOption.FALSE) + @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) + @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.EVICT }) private Set roles; - + + @ElementCollection + @CollectionTable(name = "user_property", joinColumns = @JoinColumn(name = "user_id", nullable = false)) + @MapKeyColumn(name = "property", length = 255) + @Column(name = "property_value", length = Integer.MAX_VALUE) + @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.EVICT }) private Map userProperties; - + + @Transient private List proficientLocales = null; - + + @Transient private String parsedProficientLocalesProperty = ""; - + + @ManyToOne + @JoinColumn(name = "creator", nullable = false) private User creator; - + + @Column(name = "date_created", nullable = false, length = 19) private Date dateCreated; - + + @ManyToOne + @JoinColumn(name = "changed_by") private User changedBy; - + + @Column(name = "date_changed", length = 19) private Date dateChanged; - + + @Column(name = "retired", nullable = false, length = 1) private boolean retired; - + + @ManyToOne + @JoinColumn(name = "retired_by") private User retiredBy; - + + @Column(name = "date_retired", length = 19) private Date dateRetired; - + + @Column(name = "retire_reason", length = 255) private String retireReason; // Constructors diff --git a/api/src/main/java/org/openmrs/aop/RequiredDataAdvice.java b/api/src/main/java/org/openmrs/aop/RequiredDataAdvice.java index c6b256df2f1b..348cad8fae1b 100644 --- a/api/src/main/java/org/openmrs/aop/RequiredDataAdvice.java +++ b/api/src/main/java/org/openmrs/aop/RequiredDataAdvice.java @@ -125,9 +125,8 @@ public void before(Method method, Object[] args, Object target) throws Throwable other = (String) args[1]; } - ValidateUtil.validate(mainArgument); - recursivelyHandle(SaveHandler.class, (OpenmrsObject) mainArgument, other); + ValidateUtil.validate(mainArgument); } // if the first argument is a list of openmrs objects, handle them all now else if (Reflect.isCollection(mainArgument) && isOpenmrsObjectCollection(mainArgument)) { @@ -145,9 +144,8 @@ else if (Reflect.isCollection(mainArgument) && isOpenmrsObjectCollection(mainArg Collection openmrsObjects = (Collection) mainArgument; for (OpenmrsObject object : openmrsObjects) { - ValidateUtil.validate(object); - recursivelyHandle(SaveHandler.class, object, other); + ValidateUtil.validate(object); } } diff --git a/api/src/main/java/org/openmrs/api/OrderService.java b/api/src/main/java/org/openmrs/api/OrderService.java index 80173581afaf..fb8ef6e31274 100644 --- a/api/src/main/java/org/openmrs/api/OrderService.java +++ b/api/src/main/java/org/openmrs/api/OrderService.java @@ -834,6 +834,19 @@ public Order discontinueOrder(Order orderToDiscontinue, String reasonNonCoded, D @Authorized({ PrivilegeConstants.EDIT_ORDERS, PrivilegeConstants.ADD_ORDERS }) public OrderGroup saveOrderGroup(OrderGroup orderGroup) throws APIException; + /** + * Saves an order group with a specific order context + * + * @param orderGroup the order group to be saved + * @param orderContext the order context data transfer object containing care setting and + * the order type to save with the order group + * @return the order group that was saved with the specified order context data + * @since 2.7.0 + * @throws APIException + */ + @Authorized({ PrivilegeConstants.EDIT_ORDERS, PrivilegeConstants.ADD_ORDERS }) + public OrderGroup saveOrderGroup(OrderGroup orderGroup, OrderContext orderContext) throws APIException; + /** * Fetches all order groups for the specified patient * diff --git a/api/src/main/java/org/openmrs/api/ProviderService.java b/api/src/main/java/org/openmrs/api/ProviderService.java index 7015c048f582..fe041a074ff8 100644 --- a/api/src/main/java/org/openmrs/api/ProviderService.java +++ b/api/src/main/java/org/openmrs/api/ProviderService.java @@ -36,14 +36,13 @@ public interface ProviderService extends OpenmrsService { * @return a list of provider objects. * Should get all providers */ - @Authorized( { PrivilegeConstants.GET_PROVIDERS }) public List getAllProviders(); /** - * Gets all Provider + * Gets all providers * - * @param includeRetired - whether or not to include retired Provider + * @param includeRetired - if true, retired providers are also included * Should get all providers that are unretired */ @Authorized( { PrivilegeConstants.GET_PROVIDERS }) @@ -225,14 +224,25 @@ public List getProviders(String query, Integer start, Integer length, public ProviderAttributeType getProviderAttributeType(Integer providerAttributeTypeId); /** - * Get a provider attribute type by it's uuid + * Get a provider attribute type by its uuid * * @param uuid the uuid of the provider attribute type * @return the provider attribute type for the given uuid - * Should get the provider attribute type by it's uuid + * Should get the provider attribute type by its uuid */ public ProviderAttributeType getProviderAttributeTypeByUuid(String uuid); + /** + * Get a provider attribute type by its name + * + * @param name the name of the provider attribute type + * @return the provider attribute type for the given name + * Should get the provider attribute type by its name + * @since 2.7.0, 2.6.3 + */ + @Authorized({PrivilegeConstants.GET_PROVIDER_ATTRIBUTE_TYPES}) + public ProviderAttributeType getProviderAttributeTypeByName(String name); + /** * Get a provider attribute by it's providerAttributeID * @@ -243,11 +253,11 @@ public List getProviders(String query, Integer start, Integer length, public ProviderAttribute getProviderAttribute(Integer providerAttributeID); /** - * Get a provider attribute by it's providerAttributeUuid + * Get a provider attribute by its providerAttributeUuid * * @param uuid the provider attribute uuid of the providerAttribute * @return the provider attribute for the given providerAttributeUuid - * Should get the provider attribute by it's providerAttributeUuid + * Should get the provider attribute by its providerAttributeUuid */ public ProviderAttribute getProviderAttributeByUuid(String uuid); diff --git a/api/src/main/java/org/openmrs/api/UserService.java b/api/src/main/java/org/openmrs/api/UserService.java index 5dedac6aae09..932f261206b7 100644 --- a/api/src/main/java/org/openmrs/api/UserService.java +++ b/api/src/main/java/org/openmrs/api/UserService.java @@ -35,6 +35,8 @@ * @see org.openmrs.api.context.Context */ public interface UserService extends OpenmrsService { + + public static final String ADMIN_PASSWORD_LOCKED_PROPERTY = "admin_password_locked"; /** * Create user with given password. @@ -318,16 +320,17 @@ public interface UserService extends OpenmrsService { /** * Changes the current user's password. * - * @param pw current password - * @param pw2 new password + * @param oldPassword current password + * @param newPassword new password * @throws APIException * Should match on correctly hashed sha1 stored password * Should match on incorrectly hashed sha1 stored password * Should match on sha512 hashed password * Should be able to update password multiple times + * Should respect locking via runtime properties */ @Logging(ignoredArgumentIndexes = { 0, 1 }) - public void changePassword(String pw, String pw2) throws APIException; + public void changePassword(String oldPassword, String newPassword) throws APIException; /** * Changes password of {@link User} passed in diff --git a/api/src/main/java/org/openmrs/api/ValidationException.java b/api/src/main/java/org/openmrs/api/ValidationException.java index 296efa868067..2f85997f899f 100644 --- a/api/src/main/java/org/openmrs/api/ValidationException.java +++ b/api/src/main/java/org/openmrs/api/ValidationException.java @@ -92,7 +92,7 @@ public Errors getErrors() { return errors; } - /** + /**message.prop * @since 1.11 */ public void setErrors(Errors errors) { diff --git a/api/src/main/java/org/openmrs/api/context/Context.java b/api/src/main/java/org/openmrs/api/context/Context.java index df667e819fbe..154bbaa39813 100644 --- a/api/src/main/java/org/openmrs/api/context/Context.java +++ b/api/src/main/java/org/openmrs/api/context/Context.java @@ -374,19 +374,7 @@ public static void refreshAuthenticatedUser() { public static void becomeUser(String systemId) throws ContextAuthenticationException { log.info("systemId: {}", systemId); - User user = getUserContext().becomeUser(systemId); - - // if assuming identity procedure finished successfully, we should change context locale parameter - Locale locale = null; - if (user.getUserProperties().containsKey(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCALE)) { - String localeString = user.getUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCALE); - locale = LocaleUtility.fromSpecification(localeString); - } - // when locale parameter is not valid or does not exist - if (locale == null) { - locale = LocaleUtility.getDefaultLocale(); - } - Context.setLocale(locale); + getUserContext().becomeUser(systemId); } /** @@ -699,7 +687,12 @@ public static boolean isAuthenticated() { if (Daemon.isDaemonThread()) { return true; } else { - return getAuthenticatedUser() != null; + try { + return getAuthenticatedUser() != null; + } catch (APIException e) { + log.info("Could not get authenticated user inside called to isAuthenticated(), assuming no user context has been defined", e); + return false; + } } } diff --git a/api/src/main/java/org/openmrs/api/context/Daemon.java b/api/src/main/java/org/openmrs/api/context/Daemon.java index d212cfc866ea..2fe6c82e2d62 100644 --- a/api/src/main/java/org/openmrs/api/context/Daemon.java +++ b/api/src/main/java/org/openmrs/api/context/Daemon.java @@ -89,7 +89,11 @@ public void run() { exceptionThrown = e; } finally { - Context.closeSession(); + try { + Context.closeSession(); + } finally { + isDaemonThread.remove(); + } } } }; @@ -151,7 +155,7 @@ public void run() { if (!CollectionUtils.isEmpty(roleNames)) { List roles = roleNames.stream().map(roleName -> Context.getUserService().getRole(roleName)).collect(Collectors.toList()); - roles.forEach(role -> user.addRole(role)); + roles.forEach(user::addRole); } returnedObject = Context.getUserService().createUser(user, password); @@ -160,7 +164,11 @@ public void run() { exceptionThrown = e; } finally { - Context.closeSession(); + try { + Context.closeSession(); + } finally { + isDaemonThread.remove(); + } } } }; @@ -214,7 +222,11 @@ public void run() { exceptionThrown = e; } finally { - Context.closeSession(); + try { + Context.closeSession(); + } finally { + isDaemonThread.remove(); + } } } @@ -266,7 +278,11 @@ public void run() { runnable.run(); } finally { - Context.closeSession(); + try { + Context.closeSession(); + } finally { + isDaemonThread.remove(); + } } } }; @@ -311,7 +327,11 @@ public void run() { exceptionThrown = e; } finally { - Context.closeSession(); + try { + Context.closeSession(); + } finally { + isDaemonThread.remove(); + } } } }; @@ -362,7 +382,11 @@ public void run() { runnable.run(); } finally { - Context.closeSession(); + try { + Context.closeSession(); + } finally { + isDaemonThread.remove(); + } } } }; diff --git a/api/src/main/java/org/openmrs/api/context/ServiceContext.java b/api/src/main/java/org/openmrs/api/context/ServiceContext.java index 9533f26ba423..b84f835ae9cb 100644 --- a/api/src/main/java/org/openmrs/api/context/ServiceContext.java +++ b/api/src/main/java/org/openmrs/api/context/ServiceContext.java @@ -759,7 +759,9 @@ public void setModuleService(List params) { Object classInstance = params.get(1); if (classString == null || classInstance == null) { - throw new APIException("service.unable.find", (Object[]) null); + throw new APIException( + String.format("Unable to find service as unexpected null value found for class [%s] or instance [%s]", + classString, classInstance)); } Class cls = null; @@ -792,7 +794,7 @@ public void setModuleService(List params) { } } catch (ClassNotFoundException e) { - throw new APIException("service.unable.set", new Object[] { classString }, e); + throw new APIException("Unable to find service as class not found: " + classString, e); } // add this module service to the normal list of services diff --git a/api/src/main/java/org/openmrs/api/context/UserContext.java b/api/src/main/java/org/openmrs/api/context/UserContext.java index 67b87a00c92c..c98ff9117309 100755 --- a/api/src/main/java/org/openmrs/api/context/UserContext.java +++ b/api/src/main/java/org/openmrs/api/context/UserContext.java @@ -25,6 +25,7 @@ import org.openmrs.UserSessionListener.Event; import org.openmrs.UserSessionListener.Status; import org.openmrs.api.APIAuthenticationException; +import org.openmrs.api.LocationService; import org.openmrs.util.LocaleUtility; import org.openmrs.util.OpenmrsConstants; import org.openmrs.util.RoleConstants; @@ -122,6 +123,7 @@ public Authenticated authenticate(Credentials credentials) } setUserLocation(true); + setUserLocale(true); log.debug("Authenticated as: {}", this.user); @@ -142,6 +144,7 @@ public void refreshAuthenticatedUser() { user = Context.getUserService().getUser(user.getUserId()); //update the stored location in the user's session setUserLocation(false); + setUserLocale(false); } } @@ -181,8 +184,9 @@ public User becomeUser(String systemId) throws ContextAuthenticationException { this.user = userToBecome; - //update the user's location + //update the user's location and locale setUserLocation(false); + setUserLocale(false); log.debug("Becoming user: {}", user); @@ -440,35 +444,59 @@ private void setUserLocation(boolean useDefault) { } // intended to be when the user initially authenticates - if (this.locationId == null || useDefault) { - Integer defaultLocationId = getDefaultLocationId(this.user); - - if (useDefault || defaultLocationId != null) { - this.locationId = defaultLocationId; - } + if (this.locationId == null && useDefault) { + this.locationId = getDefaultLocationId(this.user); } } + + /** + * Convenience method that sets the default locale used by the currently authenticated user, using + * the value of the user's default local property + */ + private void setUserLocale(boolean useDefault) { + // local should be null if no user is logged in + if (this.user == null) { + this.locale = null; + return; + } + + // intended to be when the user initially authenticates + if (user.getUserProperties().containsKey("defaultLocale")) { + String localeString = user.getUserProperty("defaultLocale"); + locale = LocaleUtility.fromSpecification(localeString); + } + + if (locale == null && useDefault) { + locale = LocaleUtility.getDefaultLocale(); + } + + } - private Integer getDefaultLocationId(User user) { - String locationId = user.getUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCATION); - if (StringUtils.isNotBlank(locationId)) { + protected Integer getDefaultLocationId(User user) { + String defaultLocation = user.getUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCATION); + if (StringUtils.isNotBlank(defaultLocation)) { + LocationService ls = Context.getLocationService(); //only go ahead if it has actually changed OR if wasn't set before try { - int defaultId = Integer.parseInt(locationId); + int defaultId = Integer.parseInt(defaultLocation); if (this.locationId == null || this.locationId != defaultId) { // validate that the id is a valid id - if (Context.getLocationService().getLocation(defaultId) != null) { + if (ls.getLocation(defaultId) != null) { return defaultId; - } else { - log.warn("The default location for user '{}' is set to '{}', which is not a valid location", - user.getUserId(), locationId); } } } - catch (NumberFormatException e) { - log.warn("The value of the default Location property of the user with id: {} should be an integer", - user.getUserId(), e); + catch (NumberFormatException ignored) { } + + Location possibleLocation = ls.getLocationByUuid(defaultLocation); + + if (possibleLocation != null && (this.locationId == null || !this.locationId.equals(possibleLocation.getId()))) { + return possibleLocation.getId(); + } + + log.warn("The default location for user '{}' is set to '{}', which is not a valid location", + user.getUsername(), defaultLocation); } return null; diff --git a/api/src/main/java/org/openmrs/api/db/ProviderDAO.java b/api/src/main/java/org/openmrs/api/db/ProviderDAO.java index fa474839fa0a..e755840e118b 100644 --- a/api/src/main/java/org/openmrs/api/db/ProviderDAO.java +++ b/api/src/main/java/org/openmrs/api/db/ProviderDAO.java @@ -95,6 +95,11 @@ public List getProviders(String name, Map serializationClass; - + + @Column(name = "serialized_data", length = 16777215) private String serializedData; /** diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateAdministrationDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateAdministrationDAO.java index 8c1d269761ab..3a0a6a217541 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateAdministrationDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateAdministrationDAO.java @@ -9,21 +9,22 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import java.sql.Statement; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import org.hibernate.Criteria; import org.hibernate.FlushMode; import org.hibernate.MappingException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.boot.Metadata; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.jdbc.Work; import org.hibernate.mapping.Column; @@ -93,56 +94,89 @@ public String getGlobalProperty(String propertyName) throws DAOException { return gp.getPropertyValue(); } - + /** * @see org.openmrs.api.db.AdministrationDAO#getGlobalPropertyObject(java.lang.String) */ @Override public GlobalProperty getGlobalPropertyObject(String propertyName) { + Session session = sessionFactory.getCurrentSession(); + if (isDatabaseStringComparisonCaseSensitive()) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(GlobalProperty.class); - return (GlobalProperty) criteria.add(Restrictions.eq(PROPERTY, propertyName).ignoreCase()) - .uniqueResult(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GlobalProperty.class); + Root root = query.from(GlobalProperty.class); + + Predicate condition = (propertyName != null) + ? cb.equal(cb.lower(root.get(PROPERTY)), propertyName.toLowerCase()) + : cb.isNull(root.get(PROPERTY)); + + query.where(condition); + + return session.createQuery(query).uniqueResult(); } else { - return (GlobalProperty) sessionFactory.getCurrentSession().get(GlobalProperty.class, propertyName); + return session.get(GlobalProperty.class, propertyName); } } - + @Override public GlobalProperty getGlobalPropertyByUuid(String uuid) throws DAOException { - - return (GlobalProperty) sessionFactory.getCurrentSession() - .createQuery("from GlobalProperty t where t.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, GlobalProperty.class, uuid); } - + /** * @see org.openmrs.api.db.AdministrationDAO#getAllGlobalProperties() */ @Override - @SuppressWarnings("unchecked") public List getAllGlobalProperties() throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(GlobalProperty.class); - return criteria.addOrder(Order.asc(PROPERTY)).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GlobalProperty.class); + Root root = query.from(GlobalProperty.class); + + query.orderBy(cb.asc(root.get(PROPERTY))); + + return session.createQuery(query).getResultList(); } /** * @see org.openmrs.api.db.AdministrationDAO#getGlobalPropertiesByPrefix(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List getGlobalPropertiesByPrefix(String prefix) { - return sessionFactory.getCurrentSession().createCriteria(GlobalProperty.class) - .add(Restrictions.ilike(PROPERTY, prefix, MatchMode.START)).list(); + if (prefix == null) { + log.warn("Attempted to get global properties with a null prefix"); + return Collections.emptyList(); + } + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GlobalProperty.class); + Root root = query.from(GlobalProperty.class); + + query.where(cb.like(cb.lower(root.get(PROPERTY)), MatchMode.START.toLowerCasePattern(prefix))); + + return session.createQuery(query).getResultList(); } /** * @see org.openmrs.api.db.AdministrationDAO#getGlobalPropertiesBySuffix(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List getGlobalPropertiesBySuffix(String suffix) { - return sessionFactory.getCurrentSession().createCriteria(GlobalProperty.class) - .add(Restrictions.ilike(PROPERTY, suffix, MatchMode.END)).list(); + if (suffix == null) { + log.warn("Attempted to get global properties with a null suffix"); + return Collections.emptyList(); + } + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GlobalProperty.class); + Root root = query.from(GlobalProperty.class); + + query.where(cb.like(cb.lower(root.get(PROPERTY)), MatchMode.END.toLowerCasePattern(suffix))); + + return session.createQuery(query).getResultList(); } /** diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateCohortDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateCohortDAO.java index f6c39477284b..6d5182fa1564 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateCohortDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateCohortDAO.java @@ -9,16 +9,17 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; import java.util.Date; import java.util.List; -import org.hibernate.Criteria; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.CriteriaSpecification; -import org.hibernate.criterion.Disjunction; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; import org.openmrs.Cohort; import org.openmrs.CohortMembership; import org.openmrs.api.db.CohortDAO; @@ -50,42 +51,48 @@ public void setSessionFactory(SessionFactory sessionFactory) { */ @Override public Cohort getCohort(Integer id) throws DAOException { - return (Cohort) sessionFactory.getCurrentSession().get(Cohort.class, id); + return sessionFactory.getCurrentSession().get(Cohort.class, id); } /** * @see org.openmrs.api.db.CohortDAO#getCohortsContainingPatientId(Integer, boolean, Date) */ @Override - @SuppressWarnings("unchecked") public List getCohortsContainingPatientId(Integer patientId, boolean includeVoided, - Date asOfDate) throws DAOException { - Disjunction orEndDate = Restrictions.disjunction(); - orEndDate.add(Restrictions.isNull("m.endDate")); - orEndDate.add(Restrictions.gt("m.endDate", asOfDate)); + Date asOfDate) throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Cohort.class); + Root root = cq.from(Cohort.class); + + Join membershipJoin = root.join("memberships"); + + List predicates = new ArrayList<>(); - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Cohort.class); - criteria.createAlias("memberships", "m"); if (asOfDate != null) { - criteria.add(Restrictions.le("m.startDate", asOfDate)); - criteria.add(orEndDate); + predicates.add(cb.lessThanOrEqualTo(membershipJoin.get("startDate"), asOfDate)); + + Predicate endDateNullPredicate = cb.isNull(membershipJoin.get("endDate")); + Predicate endDateGtPredicate = cb.greaterThan(membershipJoin.get("endDate"), asOfDate); + predicates.add(cb.or(endDateNullPredicate, endDateGtPredicate)); } - criteria.add(Restrictions.eq("m.patientId", patientId)); - criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); - + predicates.add(cb.equal(membershipJoin.get("patientId"), patientId)); + if (!includeVoided) { - criteria.add(Restrictions.eq(VOIDED, includeVoided)); + predicates.add(cb.equal(root.get(VOIDED), includeVoided)); } - return criteria.list(); - } + cq.distinct(true).where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); + } + /** * @see org.openmrs.api.db.CohortDAO#getCohortByUuid(java.lang.String) */ @Override public Cohort getCohortByUuid(String uuid) { - return (Cohort) sessionFactory.getCurrentSession().createQuery("from Cohort c where c.uuid = :uuid").setString( - "uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Cohort.class, uuid); } /** @@ -93,9 +100,7 @@ public Cohort getCohortByUuid(String uuid) { */ @Override public CohortMembership getCohortMembershipByUuid(String uuid) { - return (CohortMembership) sessionFactory.getCurrentSession() - .createQuery("from CohortMembership m where m.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, CohortMembership.class, uuid); } /** @@ -106,47 +111,56 @@ public Cohort deleteCohort(Cohort cohort) throws DAOException { sessionFactory.getCurrentSession().delete(cohort); return null; } - + /** * @see org.openmrs.api.db.CohortDAO#getCohorts(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List getCohorts(String nameFragment) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Cohort.class); - criteria.add(Restrictions.ilike("name", nameFragment, MatchMode.ANYWHERE)); - criteria.addOrder(Order.asc("name")); - return criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Cohort.class); + Root root = cq.from(Cohort.class); + + cq.where(cb.like(cb.lower(root.get("name")), + MatchMode.ANYWHERE.toLowerCasePattern(nameFragment))); + cq.orderBy(cb.asc(root.get("name"))); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.CohortDAO#getAllCohorts(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllCohorts(boolean includeVoided) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Cohort.class); - - criteria.addOrder(Order.asc("name")); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Cohort.class); + Root root = cq.from(Cohort.class); + if (!includeVoided) { - criteria.add(Restrictions.eq(VOIDED, false)); + cq.where(cb.isFalse(root.get(VOIDED))); } - - return (List) criteria.list(); + + cq.orderBy(cb.asc(root.get("name"))); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.CohortDAO#getCohort(java.lang.String) */ @Override public Cohort getCohort(String name) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Cohort.class); - - criteria.add(Restrictions.eq("name", name)); - criteria.add(Restrictions.eq(VOIDED, false)); - - return (Cohort) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Cohort.class); + Root root = cq.from(Cohort.class); + + cq.where(cb.equal(root.get("name"), name), cb.isFalse(root.get(VOIDED))); + + return session.createQuery(cq).uniqueResult(); } /** @@ -157,22 +171,34 @@ public Cohort saveCohort(Cohort cohort) throws DAOException { sessionFactory.getCurrentSession().saveOrUpdate(cohort); return cohort; } - + @Override public List getCohortMemberships(Integer patientId, Date activeOnDate, boolean includeVoided) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(CohortMembership.class); - criteria.add(Restrictions.eq("patientId", patientId)); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(CohortMembership.class); + Root root = cq.from(CohortMembership.class); + + List predicates = new ArrayList<>(); + + predicates.add(cb.equal(root.get("patientId"), patientId)); + if (activeOnDate != null) { - criteria.add(Restrictions.le("startDate", activeOnDate)); - criteria.add(Restrictions.or( - Restrictions.isNull("endDate"), - Restrictions.ge("endDate", activeOnDate) - )); + predicates.add(cb.lessThanOrEqualTo(root.get("startDate"), activeOnDate)); + + Predicate endDateIsNull = cb.isNull(root.get("endDate")); + Predicate endDateIsGreater = cb.greaterThanOrEqualTo(root.get("endDate"), activeOnDate); + + predicates.add(cb.or(endDateIsNull, endDateIsGreater)); } + if (!includeVoided) { - criteria.add(Restrictions.eq(VOIDED, false)); + predicates.add(cb.isFalse(root.get(VOIDED))); } - return criteria.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } @Override diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConceptDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConceptDAO.java index 361b280e0f41..ca5dd5ea2ceb 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConceptDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConceptDAO.java @@ -9,6 +9,13 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -21,21 +28,13 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; import org.hibernate.FlushMode; -import org.hibernate.Query; -import org.hibernate.SQLQuery; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Criterion; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Restrictions; -import org.hibernate.criterion.SimpleExpression; -import org.hibernate.transform.DistinctRootEntityResultTransformer; import org.openmrs.Concept; import org.openmrs.ConceptAnswer; import org.openmrs.ConceptAttribute; @@ -58,6 +57,7 @@ import org.openmrs.ConceptStopWord; import org.openmrs.Drug; import org.openmrs.DrugIngredient; +import org.openmrs.DrugReferenceMap; import org.openmrs.OpenmrsObject; import org.openmrs.api.APIException; import org.openmrs.api.ConceptService; @@ -92,27 +92,33 @@ public class HibernateConceptDAO implements ConceptDAO { public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptComplex(java.lang.Integer) */ @Override public ConceptComplex getConceptComplex(Integer conceptId) { ConceptComplex cc; - Object obj = sessionFactory.getCurrentSession().get(ConceptComplex.class, conceptId); + Session session = sessionFactory.getCurrentSession(); + Object obj = session.get(ConceptComplex.class, conceptId); // If Concept has already been read & cached, we may get back a Concept instead of // ConceptComplex. If this happens, we need to clear the object from the cache // and re-fetch it as a ConceptComplex if (obj != null && !obj.getClass().equals(ConceptComplex.class)) { // remove from cache - sessionFactory.getCurrentSession().evict(obj); + session.detach(obj); + // session.get() did not work here, we need to perform a query to get a ConceptComplex - Query query = sessionFactory.getCurrentSession().createQuery("from ConceptComplex where conceptId = :conceptId") - .setParameter("conceptId", conceptId); - obj = query.uniqueResult(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptComplex.class); + Root root = cq.from(ConceptComplex.class); + + cq.where(cb.equal(root.get("conceptId"), conceptId)); + + obj = session.createQuery(cq).uniqueResult(); } cc = (ConceptComplex) obj; - + return cc; } @@ -147,10 +153,10 @@ private void insertRowIntoSubclassIfNecessary(Concept concept) { String select = "SELECT 1 from concept_numeric WHERE concept_id = :conceptId"; Query query = sessionFactory.getCurrentSession().createSQLQuery(select); - query.setInteger("conceptId", concept.getConceptId()); + query.setParameter("conceptId", concept.getConceptId()); // Converting to concept numeric: A single concept row exists, but concept numeric has not been populated yet. - if (query.uniqueResult() == null) { + if (JpaUtils.getSingleResultOrNull(query) == null) { // we have to evict the current concept out of the session because // the user probably had to change the class of this object to get it // to now be a numeric @@ -163,7 +169,7 @@ private void insertRowIntoSubclassIfNecessary(Concept concept) { String insert = "INSERT INTO concept_numeric (concept_id, allow_decimal) VALUES (:conceptId, false)"; query = sessionFactory.getCurrentSession().createSQLQuery(insert); - query.setInteger("conceptId", concept.getConceptId()); + query.setParameter("conceptId", concept.getConceptId()); query.executeUpdate(); } else { @@ -181,10 +187,10 @@ else if (concept instanceof ConceptComplex) { String select = "SELECT 1 FROM concept_complex WHERE concept_id = :conceptId"; Query query = sessionFactory.getCurrentSession().createSQLQuery(select); - query.setInteger("conceptId", concept.getConceptId()); + query.setParameter("conceptId", concept.getConceptId()); // Converting to concept complex: A single concept row exists, but concept complex has not been populated yet. - if (query.uniqueResult() == null) { + if (JpaUtils.getSingleResultOrNull(query) == null) { // we have to evict the current concept out of the session because // the user probably had to change the class of this object to get it // to now be a ConceptComplex @@ -198,7 +204,7 @@ else if (concept instanceof ConceptComplex) { // Add an empty row into the concept_complex table String insert = "INSERT INTO concept_complex (concept_id) VALUES (:conceptId)"; query = sessionFactory.getCurrentSession().createSQLQuery(insert); - query.setInteger("conceptId", concept.getConceptId()); + query.setParameter("conceptId", concept.getConceptId()); query.executeUpdate(); } else { @@ -223,7 +229,7 @@ else if (concept instanceof ConceptComplex) { private void deleteSubclassConcept(String tableName, Integer conceptId) { String delete = "DELETE FROM " + tableName + " WHERE concept_id = :conceptId"; Query query = sessionFactory.getCurrentSession().createSQLQuery(delete); - query.setInteger("conceptId", conceptId); + query.setParameter("conceptId", conceptId); query.executeUpdate(); } @@ -240,7 +246,7 @@ public void purgeConcept(Concept concept) throws DAOException { */ @Override public Concept getConcept(Integer conceptId) throws DAOException { - return (Concept) sessionFactory.getCurrentSession().get(Concept.class, conceptId); + return sessionFactory.getCurrentSession().get(Concept.class, conceptId); } /** @@ -248,7 +254,7 @@ public Concept getConcept(Integer conceptId) throws DAOException { */ @Override public ConceptName getConceptName(Integer conceptNameId) throws DAOException { - return (ConceptName) sessionFactory.getCurrentSession().get(ConceptName.class, conceptNameId); + return sessionFactory.getCurrentSession().get(ConceptName.class, conceptNameId); } /** @@ -256,7 +262,7 @@ public ConceptName getConceptName(Integer conceptNameId) throws DAOException { */ @Override public ConceptAnswer getConceptAnswer(Integer conceptAnswerId) throws DAOException { - return (ConceptAnswer) sessionFactory.getCurrentSession().get(ConceptAnswer.class, conceptAnswerId); + return sessionFactory.getCurrentSession().get(ConceptAnswer.class, conceptAnswerId); } /** @@ -313,7 +319,7 @@ public List getAllConcepts(String sortBy, boolean asc, boolean includeR hql += asc ? " asc" : " desc"; Query query = sessionFactory.getCurrentSession().createQuery(hql); - return (List) query.list(); + return (List) query.getResultList(); } /** @@ -330,45 +336,60 @@ public Drug saveDrug(Drug drug) throws DAOException { */ @Override public Drug getDrug(Integer drugId) throws DAOException { - return (Drug) sessionFactory.getCurrentSession().get(Drug.class, drugId); + return sessionFactory.getCurrentSession().get(Drug.class, drugId); } /** * @see org.openmrs.api.db.ConceptDAO#getDrugs(java.lang.String, org.openmrs.Concept, boolean) */ @Override - @SuppressWarnings("unchecked") public List getDrugs(String drugName, Concept concept, boolean includeRetired) throws DAOException { - Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug"); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Drug.class); + Root drugRoot = cq.from(Drug.class); + + List predicates = new ArrayList<>(); + if (!includeRetired) { - searchCriteria.add(Restrictions.eq("drug.retired", false)); + predicates.add(cb.isFalse(drugRoot.get("retired"))); } + if (concept != null) { - searchCriteria.add(Restrictions.eq("drug.concept", concept)); + predicates.add(cb.equal(drugRoot.get("concept"), concept)); } + if (drugName != null) { - SimpleExpression eq = Restrictions.eq("drug.name", drugName); if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) { - eq = eq.ignoreCase(); + predicates.add(cb.equal(cb.lower(drugRoot.get("name")), MatchMode.EXACT.toLowerCasePattern(drugName))); + } else { + predicates.add(cb.equal(drugRoot.get("name"), MatchMode.EXACT.toCaseSensitivePattern(drugName))); } - searchCriteria.add(eq); } - return (List) searchCriteria.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getDrugsByIngredient(org.openmrs.Concept) */ @Override - @SuppressWarnings("unchecked") public List getDrugsByIngredient(Concept ingredient) { - Criteria searchDrugCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug"); - Criterion rhs = Restrictions.eq("drug.concept", ingredient); - searchDrugCriteria.createAlias("ingredients", "ingredients"); - Criterion lhs = Restrictions.eq("ingredients.ingredient", ingredient); - searchDrugCriteria.add(Restrictions.or(lhs, rhs)); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Drug.class); + Root drugRoot = cq.from(Drug.class); - return (List) searchDrugCriteria.list(); + Join ingredientJoin = drugRoot.join("ingredients"); + + Predicate rhs = cb.equal(drugRoot.get("concept"), ingredient); + Predicate lhs = cb.equal(ingredientJoin.get("ingredient"), ingredient); + + cq.where(cb.or(lhs, rhs)); + + return session.createQuery(cq).getResultList(); } /** @@ -390,36 +411,42 @@ public List getDrugs(final String phrase) throws DAOException { */ @Override public ConceptClass getConceptClass(Integer i) throws DAOException { - return (ConceptClass) sessionFactory.getCurrentSession().get(ConceptClass.class, i); + return sessionFactory.getCurrentSession().get(ConceptClass.class, i); } /** * @see org.openmrs.api.db.ConceptDAO#getConceptClasses(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List getConceptClasses(String name) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptClass.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptClass.class); + Root root = cq.from(ConceptClass.class); + if (name != null) { - crit.add(Restrictions.eq("name", name)); + cq.where(cb.equal(root.get("name"), name)); } - return crit.list(); + + return session.createQuery(cq).getResultList(); } /** * @see org.openmrs.api.db.ConceptDAO#getAllConceptClasses(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllConceptClasses(boolean includeRetired) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptClass.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptClass.class); + Root root = cq.from(ConceptClass.class); + // Minor bug - was assigning includeRetired instead of evaluating if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - return crit.list(); + + return session.createQuery(cq).getResultList(); } /** @@ -452,49 +479,54 @@ public void deleteConceptNameTag(ConceptNameTag cnt) throws DAOException { */ @Override public ConceptDatatype getConceptDatatype(Integer i) { - return (ConceptDatatype) sessionFactory.getCurrentSession().get(ConceptDatatype.class, i); + return sessionFactory.getCurrentSession().get(ConceptDatatype.class, i); } - /** - * @see org.openmrs.api.db.ConceptDAO#getAllConceptDatatypes(boolean) - */ @Override - @SuppressWarnings("unchecked") public List getAllConceptDatatypes(boolean includeRetired) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptDatatype.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptDatatype.class); + Root root = cq.from(ConceptDatatype.class); + if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - return crit.list(); + + return session.createQuery(cq).getResultList(); } - + /** * @param name the name of the ConceptDatatype * @return a List of ConceptDatatype whose names start with the passed name */ - @SuppressWarnings("unchecked") public List getConceptDatatypes(String name) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptDatatype.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptDatatype.class); + Root root = cq.from(ConceptDatatype.class); + if (name != null) { - crit.add(Restrictions.like("name", name, MatchMode.START)); + cq.where(cb.like(root.get("name"), MatchMode.START.toCaseSensitivePattern(name))); } - - return crit.list(); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptDatatypeByName(String) */ @Override public ConceptDatatype getConceptDatatypeByName(String name) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptDatatype.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptDatatype.class); + Root root = cq.from(ConceptDatatype.class); + if (name != null) { - criteria.add(Restrictions.eq("name", name)); + cq.where(cb.equal(root.get("name"), name)); } - return (ConceptDatatype) criteria.uniqueResult(); + return session.createQuery(cq).uniqueResult(); } /** @@ -530,7 +562,7 @@ public ConceptNumeric getConceptNumeric(Integer i) { // session.get() did not work here, we need to perform a query to get a ConceptNumeric Query query = sessionFactory.getCurrentSession().createQuery("from ConceptNumeric where conceptId = :conceptId") .setParameter("conceptId", i); - obj = query.uniqueResult(); + obj = JpaUtils.getSingleResultOrNull(query); } cn = (ConceptNumeric) obj; @@ -672,40 +704,54 @@ public List getConceptsByAnswer(Concept concept) { Query query = sessionFactory.getCurrentSession().createQuery(q); query.setParameter("answer", concept); - return query.list(); + return query.getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getPrevConcept(org.openmrs.Concept) */ @Override - @SuppressWarnings("unchecked") public Concept getPrevConcept(Concept c) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Concept.class); + Root root = cq.from(Concept.class); + Integer i = c.getConceptId(); - - List concepts = sessionFactory.getCurrentSession().createCriteria(Concept.class).add( - Restrictions.lt("conceptId", i)).addOrder(Order.desc("conceptId")).setFetchSize(1).list(); - + + cq.where(cb.lessThan(root.get("conceptId"), i)); + cq.orderBy(cb.desc(root.get("conceptId"))); + + List concepts = session.createQuery(cq).setMaxResults(1).getResultList(); + if (concepts.isEmpty()) { return null; } + return concepts.get(0); } - + /** * @see org.openmrs.api.db.ConceptDAO#getNextConcept(org.openmrs.Concept) */ @Override - @SuppressWarnings("unchecked") public Concept getNextConcept(Concept c) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Concept.class); + Root root = cq.from(Concept.class); + Integer i = c.getConceptId(); - - List concepts = sessionFactory.getCurrentSession().createCriteria(Concept.class).add( - Restrictions.gt("conceptId", i)).addOrder(Order.asc("conceptId")).setMaxResults(1).list(); - + + cq.where(cb.greaterThan(root.get("conceptId"), i)); + cq.orderBy(cb.asc(root.get("conceptId"))); + + List concepts = session.createQuery(cq).setMaxResults(1).getResultList(); + if (concepts.isEmpty()) { return null; } + return concepts.get(0); } @@ -717,7 +763,7 @@ public Concept getNextConcept(Concept c) { public List getConceptsWithDrugsInFormulary() { Query query = sessionFactory.getCurrentSession().createQuery( "select distinct concept from Drug d where d.retired = false"); - return query.list(); + return query.getResultList(); } /** @@ -744,20 +790,22 @@ public ConceptProposal saveConceptProposal(ConceptProposal cp) throws DAOExcepti public void purgeConceptProposal(ConceptProposal cp) throws DAOException { sessionFactory.getCurrentSession().delete(cp); } - + /** * @see org.openmrs.api.db.ConceptDAO#getAllConceptProposals(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllConceptProposals(boolean includeCompleted) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptProposal.class); + Root root = cq.from(ConceptProposal.class); + if (!includeCompleted) { - crit.add(Restrictions.eq("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED)); + cq.where(cb.equal(root.get("state"), OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED)); } - crit.addOrder(Order.asc("originalText")); - return crit.list(); + cq.orderBy(cb.asc(root.get("originalText"))); + return session.createQuery(cq).getResultList(); } /** @@ -765,54 +813,76 @@ public List getAllConceptProposals(boolean includeCompleted) th */ @Override public ConceptProposal getConceptProposal(Integer conceptProposalId) throws DAOException { - return (ConceptProposal) sessionFactory.getCurrentSession().get(ConceptProposal.class, conceptProposalId); + return sessionFactory.getCurrentSession().get(ConceptProposal.class, conceptProposalId); } /** * @see org.openmrs.api.db.ConceptDAO#getConceptProposals(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List getConceptProposals(String text) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class); - crit.add(Restrictions.eq("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED)); - crit.add(Restrictions.eq("originalText", text)); - return crit.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptProposal.class); + Root root = cq.from(ConceptProposal.class); + + Predicate stateCondition = cb.equal(root.get("state"), OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED); + Predicate textCondition = cb.equal(root.get("originalText"), text); + + cq.where(cb.and(stateCondition, textCondition)); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getProposedConcepts(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List getProposedConcepts(String text) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class); - crit.add(Restrictions.ne("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED)); - crit.add(Restrictions.eq("originalText", text)); - crit.add(Restrictions.isNotNull("mappedConcept")); - crit.setProjection(Projections.distinct(Projections.property("mappedConcept"))); - - return crit.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Concept.class); + Root root = cq.from(ConceptProposal.class); + + Predicate stateNotEqual = cb.notEqual(root.get("state"), OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED); + Predicate originalTextEqual = cb.equal(root.get("originalText"), text); + Predicate mappedConceptNotNull = cb.isNotNull(root.get("mappedConcept")); + + cq.select(root.get("mappedConcept")).distinct(true); + cq.where(stateNotEqual, originalTextEqual, mappedConceptNotNull); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptSetsByConcept(org.openmrs.Concept) */ @Override - @SuppressWarnings("unchecked") public List getConceptSetsByConcept(Concept concept) { - return sessionFactory.getCurrentSession().createCriteria(ConceptSet.class).add( - Restrictions.eq("conceptSet", concept)).addOrder(Order.asc("sortWeight")).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptSet.class); + Root root = cq.from(ConceptSet.class); + + cq.where(cb.equal(root.get("conceptSet"), concept)); + cq.orderBy(cb.asc(root.get("sortWeight"))); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getSetsContainingConcept(org.openmrs.Concept) */ @Override - @SuppressWarnings("unchecked") public List getSetsContainingConcept(Concept concept) { - return sessionFactory.getCurrentSession().createCriteria(ConceptSet.class).add(Restrictions.eq("concept", concept)) - .list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptSet.class); + Root root = cq.from(ConceptSet.class); + + cq.where(cb.equal(root.get("concept"), concept)); + + return session.createQuery(cq).getResultList(); } /** @@ -827,8 +897,8 @@ private List getParents(Concept current) throws DAOException { List parents = new ArrayList<>(); if (current != null) { Query query = sessionFactory.getCurrentSession().createQuery( - "from Concept c join c.conceptSets sets where sets.concept = ?").setEntity(0, current); - List immedParents = query.list(); + "from Concept c join c.conceptSets sets where sets.concept = ?").setParameter(0, current); + List immedParents = query.getResultList(); for (Concept c : immedParents) { parents.addAll(getParents(c)); } @@ -852,7 +922,7 @@ public Set getLocalesOfConceptNames() { Query query = sessionFactory.getCurrentSession().createQuery("select distinct locale from ConceptName"); - for (Object locale : query.list()) { + for (Object locale : query.getResultList()) { locales.add((Locale) locale); } @@ -864,22 +934,27 @@ public Set getLocalesOfConceptNames() { */ @Override public ConceptNameTag getConceptNameTag(Integer i) { - return (ConceptNameTag) sessionFactory.getCurrentSession().get(ConceptNameTag.class, i); + return sessionFactory.getCurrentSession().get(ConceptNameTag.class, i); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptNameTagByName(java.lang.String) */ @Override public ConceptNameTag getConceptNameTagByName(String name) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptNameTag.class).add( - Restrictions.eq("tag", name)); - - if (crit.list().isEmpty()) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptNameTag.class); + Root root = cq.from(ConceptNameTag.class); + + cq.where(cb.equal(root.get("tag"), name)); + + List conceptNameTags = session.createQuery(cq).getResultList(); + if (conceptNameTags.isEmpty()) { return null; } - - return (ConceptNameTag) crit.list().get(0); + + return conceptNameTags.get(0); } /** @@ -896,23 +971,24 @@ public List getAllConceptNameTags() { */ @Override public ConceptSource getConceptSource(Integer conceptSourceId) { - return (ConceptSource) sessionFactory.getCurrentSession().get(ConceptSource.class, conceptSourceId); + return sessionFactory.getCurrentSession().get(ConceptSource.class, conceptSourceId); } /** * @see org.openmrs.api.db.ConceptDAO#getAllConceptSources(boolean) */ - @Override - @SuppressWarnings("unchecked") public List getAllConceptSources(boolean includeRetired) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptSource.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptSource.class); + Root root = cq.from(ConceptSource.class); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - return criteria.list(); + + return session.createQuery(cq).getResultList(); } /** @@ -951,7 +1027,7 @@ public ConceptNameTag saveConceptNameTag(ConceptNameTag nameTag) { */ public Integer getMinConceptId() { Query query = sessionFactory.getCurrentSession().createQuery("select min(conceptId) from Concept"); - return (Integer) query.uniqueResult(); + return JpaUtils.getSingleResultOrNull(query); } /** @@ -960,7 +1036,7 @@ public Integer getMinConceptId() { @Override public Integer getMaxConceptId() { Query query = sessionFactory.getCurrentSession().createQuery("select max(conceptId) from Concept"); - return (Integer) query.uniqueResult(); + return JpaUtils.getSingleResultOrNull(query); } /** @@ -1022,92 +1098,111 @@ public void remove() { * @see org.openmrs.api.db.ConceptDAO#getConceptsByMapping(String, String, boolean) */ @Override - @SuppressWarnings("unchecked") @Deprecated public List getConceptsByMapping(String code, String sourceName, boolean includeRetired) { - Criteria criteria = createSearchConceptMapCriteria(code, sourceName, includeRetired); - criteria.setProjection(Projections.property("concept")); - return (List) criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Concept.class); + Root root = cq.from(ConceptMap.class); + + List predicates = createSearchConceptMapCriteria(cb, root, code, sourceName, includeRetired); + + cq.where(predicates.toArray(new Predicate[]{})); + + cq.select(root.get("concept")); + + Join conceptJoin = root.join("concept"); + if (includeRetired) { + cq.orderBy(cb.asc(conceptJoin.get("retired"))); + } + + return session.createQuery(cq).getResultList() + .stream().distinct().collect(Collectors.toList()); } /** * @see org.openmrs.api.db.ConceptDAO#getConceptIdsByMapping(String, String, boolean) */ @Override - @SuppressWarnings("unchecked") public List getConceptIdsByMapping(String code, String sourceName, boolean includeRetired) { - Criteria criteria = createSearchConceptMapCriteria(code, sourceName, includeRetired); - criteria.setProjection(Projections.property("concept.conceptId")); - return (List) criteria.list(); - } + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Integer.class); + Root root = cq.from(ConceptMap.class); + + List predicates = createSearchConceptMapCriteria(cb, root, code, sourceName, includeRetired); + cq.where(predicates.toArray(new Predicate[]{})); + + cq.select(root.get("concept").get("conceptId")); + + Join conceptJoin = root.join("concept"); + if (includeRetired) { + cq.orderBy(cb.asc(conceptJoin.get("retired"))); + } + + return session.createQuery(cq).getResultList() + .stream().distinct().collect(Collectors.toList()); + } + /** * @see org.openmrs.api.db.ConceptDAO#getConceptByUuid(java.lang.String) */ @Override public Concept getConceptByUuid(String uuid) { - return (Concept) sessionFactory.getCurrentSession().createQuery("from Concept c where c.uuid = :uuid").setString( - "uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Concept.class, uuid); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptClassByUuid(java.lang.String) */ @Override public ConceptClass getConceptClassByUuid(String uuid) { - return (ConceptClass) sessionFactory.getCurrentSession().createQuery("from ConceptClass cc where cc.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptClass.class, uuid); } - + @Override public ConceptAnswer getConceptAnswerByUuid(String uuid) { - return (ConceptAnswer) sessionFactory.getCurrentSession().createQuery("from ConceptAnswer cc where cc.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAnswer.class, uuid); } - + @Override public ConceptName getConceptNameByUuid(String uuid) { - return (ConceptName) sessionFactory.getCurrentSession().createQuery("from ConceptName cc where cc.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptName.class, uuid); } - + @Override public ConceptSet getConceptSetByUuid(String uuid) { - return (ConceptSet) sessionFactory.getCurrentSession().createQuery("from ConceptSet cc where cc.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptSet.class, uuid); } - + @Override public ConceptSource getConceptSourceByUuid(String uuid) { - return (ConceptSource) sessionFactory.getCurrentSession().createQuery("from ConceptSource cc where cc.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptSource.class, uuid); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptDatatypeByUuid(java.lang.String) */ @Override public ConceptDatatype getConceptDatatypeByUuid(String uuid) { - return (ConceptDatatype) sessionFactory.getCurrentSession().createQuery( - "from ConceptDatatype cd where cd.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptDatatype.class, uuid); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptNumericByUuid(java.lang.String) */ @Override public ConceptNumeric getConceptNumericByUuid(String uuid) { - return (ConceptNumeric) sessionFactory.getCurrentSession().createQuery( - "from ConceptNumeric cn where cn.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptNumeric.class, uuid); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptProposalByUuid(java.lang.String) */ @Override public ConceptProposal getConceptProposalByUuid(String uuid) { - return (ConceptProposal) sessionFactory.getCurrentSession().createQuery( - "from ConceptProposal cp where cp.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptProposal.class, uuid); } /** @@ -1115,68 +1210,75 @@ public ConceptProposal getConceptProposalByUuid(String uuid) { */ @Override public Drug getDrugByUuid(String uuid) { - return (Drug) sessionFactory.getCurrentSession().createQuery("from Drug d where d.uuid = :uuid").setString("uuid", - uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Drug.class, uuid); } - + @Override public DrugIngredient getDrugIngredientByUuid(String uuid) { - return (DrugIngredient) sessionFactory.getCurrentSession().createQuery("from DrugIngredient d where d.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, DrugIngredient.class, uuid); } /** * @see org.openmrs.api.db.ConceptDAO#getConceptUuids() */ @Override + @SuppressWarnings("unchecked") public Map getConceptUuids() { Map ret = new HashMap<>(); Query q = sessionFactory.getCurrentSession().createQuery("select conceptId, uuid from Concept"); - List list = q.list(); + List list = q.getResultList(); for (Object[] o : list) { ret.put((Integer) o[0], (String) o[1]); } return ret; } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptDescriptionByUuid(java.lang.String) */ @Override public ConceptDescription getConceptDescriptionByUuid(String uuid) { - return (ConceptDescription) sessionFactory.getCurrentSession().createQuery( - "from ConceptDescription cd where cd.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptDescription.class, uuid); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptNameTagByUuid(java.lang.String) */ @Override public ConceptNameTag getConceptNameTagByUuid(String uuid) { - return (ConceptNameTag) sessionFactory.getCurrentSession().createQuery( - "from ConceptNameTag cnt where cnt.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptNameTag.class, uuid); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptMapsBySource(ConceptSource) */ @Override - @SuppressWarnings("unchecked") public List getConceptMapsBySource(ConceptSource conceptSource) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMap.class); - criteria.createAlias("conceptReferenceTerm", "term"); - criteria.add(Restrictions.eq("term.conceptSource", conceptSource)); - return (List) criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptMap.class); + + Root root = cq.from(ConceptMap.class); + Join conceptReferenceTermJoin = root.join("conceptReferenceTerm"); + + cq.where(cb.equal(conceptReferenceTermJoin.get("conceptSource"), conceptSource)); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptSourceByName(java.lang.String) */ @Override public ConceptSource getConceptSourceByName(String conceptSourceName) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptSource.class, "source"); - criteria.add(Restrictions.eq("source.name", conceptSourceName)); - return (ConceptSource) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptSource.class); + Root root = cq.from(ConceptSource.class); + + cq.where(cb.equal(root.get("name"), conceptSourceName)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -1187,11 +1289,17 @@ public ConceptSource getConceptSourceByUniqueId(String uniqueId) { if (StringUtils.isBlank(uniqueId)) { return null; } - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptSource.class); - criteria.add(Restrictions.eq("uniqueId", uniqueId)); - return (ConceptSource) criteria.uniqueResult(); + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptSource.class); + Root root = cq.from(ConceptSource.class); + + cq.where(cb.equal(root.get("uniqueId"), uniqueId)); + + return session.createQuery(cq).uniqueResult(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptSourceByHL7Code(java.lang.String) */ @@ -1200,22 +1308,29 @@ public ConceptSource getConceptSourceByHL7Code(String hl7Code) { if (StringUtils.isBlank(hl7Code)) { return null; } - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptSource.class); - criteria.add(Restrictions.eq("hl7Code", hl7Code)); - return (ConceptSource) criteria.uniqueResult(); + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptSource.class); + Root root = cq.from(ConceptSource.class); + + cq.where(cb.equal(root.get("hl7Code"), hl7Code)); + + return session.createQuery(cq).uniqueResult(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getSavedConceptDatatype(org.openmrs.Concept) */ @Override public ConceptDatatype getSavedConceptDatatype(Concept concept) { - SQLQuery sql = sessionFactory.getCurrentSession().createSQLQuery( - "select datatype.* from " + "concept_datatype datatype, " + "concept concept " + "where " - + "datatype.concept_datatype_id = concept.datatype_id " + "and concept.concept_id=:conceptId") - .addEntity(ConceptDatatype.class); - sql.setInteger("conceptId", concept.getConceptId()); - return (ConceptDatatype) sql.uniqueResult(); + Query sql = sessionFactory.getCurrentSession().createSQLQuery( + "select datatype.* from concept_datatype datatype, concept concept where " + + "datatype.concept_datatype_id = concept.datatype_id and concept.concept_id=:conceptId") + .addEntity(ConceptDatatype.class); + sql.setParameter("conceptId", concept.getConceptId()); + + return JpaUtils.getSingleResultOrNull(sql); } /** @@ -1226,39 +1341,48 @@ public ConceptName getSavedConceptName(ConceptName conceptName) { sessionFactory.getCurrentSession().refresh(conceptName); return conceptName; } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptStopWords(java.util.Locale) */ @Override public List getConceptStopWords(Locale locale) throws DAOException { - + locale = (locale == null ? Context.getLocale() : locale); - - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptStopWord.class); - criteria.setProjection(Projections.property("value")); - criteria.add(Restrictions.eq("locale", locale)); - - return (List) criteria.list(); + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(String.class); + Root root = cq.from(ConceptStopWord.class); + + cq.select(root.get("value")); + cq.where(cb.equal(root.get("locale"), locale)); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#saveConceptStopWord(org.openmrs.ConceptStopWord) */ @Override public ConceptStopWord saveConceptStopWord(ConceptStopWord conceptStopWord) throws DAOException { if (conceptStopWord != null) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptStopWord.class); - criteria.add(Restrictions.eq("value", conceptStopWord.getValue())); - criteria.add(Restrictions.eq("locale", conceptStopWord.getLocale())); - List stopWordList = criteria.list(); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptStopWord.class); + Root root = cq.from(ConceptStopWord.class); + + cq.where(cb.and( + cb.equal(root.get("value"), conceptStopWord.getValue()), + cb.equal(root.get("locale"), conceptStopWord.getLocale()))); + + List stopWordList = session.createQuery(cq).getResultList(); + if (!stopWordList.isEmpty()) { throw new DAOException("Duplicate ConceptStopWord Entry"); } - sessionFactory.getCurrentSession().saveOrUpdate(conceptStopWord); + session.saveOrUpdate(conceptStopWord); } - return conceptStopWord; } @@ -1270,20 +1394,32 @@ public void deleteConceptStopWord(Integer conceptStopWordId) throws DAOException if (conceptStopWordId == null) { throw new DAOException("conceptStopWordId is null"); } - Object csw = sessionFactory.getCurrentSession().createCriteria(ConceptStopWord.class).add( - Restrictions.eq("conceptStopWordId", conceptStopWordId)).uniqueResult(); + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptStopWord.class); + Root root = cq.from(ConceptStopWord.class); + + cq.where(cb.equal(root.get("conceptStopWordId"), conceptStopWordId)); + + ConceptStopWord csw = session.createQuery(cq).uniqueResult(); if (csw == null) { throw new DAOException("Concept Stop Word not found or already deleted"); } - sessionFactory.getCurrentSession().delete(csw); + session.delete(csw); } - + /** * @see org.openmrs.api.db.ConceptDAO#getAllConceptStopWords() */ @Override public List getAllConceptStopWords() { - return sessionFactory.getCurrentSession().createCriteria(ConceptStopWord.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptStopWord.class); + cq.from(ConceptStopWord.class); + + return session.createQuery(cq).getResultList(); } /** @@ -1457,24 +1593,30 @@ private String[] transformToIds(final List items) { } return ids; } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptMapTypes(boolean, boolean) */ - @SuppressWarnings("unchecked") @Override public List getConceptMapTypes(boolean includeRetired, boolean includeHidden) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMapType.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptMapType.class); + Root root = cq.from(ConceptMapType.class); + + List predicates = new ArrayList<>(); if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } if (!includeHidden) { - criteria.add(Restrictions.eq("isHidden", false)); + predicates.add(cb.isFalse(root.get("isHidden"))); } - - List conceptMapTypes = criteria.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + + List conceptMapTypes = session.createQuery(cq).getResultList(); conceptMapTypes.sort(new ConceptMapTypeComparator()); - + return conceptMapTypes; } @@ -1483,26 +1625,30 @@ public List getConceptMapTypes(boolean includeRetired, boolean i */ @Override public ConceptMapType getConceptMapType(Integer conceptMapTypeId) throws DAOException { - return (ConceptMapType) sessionFactory.getCurrentSession().get(ConceptMapType.class, conceptMapTypeId); + return sessionFactory.getCurrentSession().get(ConceptMapType.class, conceptMapTypeId); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptMapTypeByUuid(java.lang.String) */ @Override public ConceptMapType getConceptMapTypeByUuid(String uuid) throws DAOException { - return (ConceptMapType) sessionFactory.getCurrentSession().createQuery( - "from ConceptMapType cmt where cmt.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptMapType.class, uuid); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptMapTypeByName(java.lang.String) */ @Override public ConceptMapType getConceptMapTypeByName(String name) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMapType.class); - criteria.add(Restrictions.ilike("name", name, MatchMode.EXACT)); - return (ConceptMapType) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptMapType.class); + Root root = cq.from(ConceptMapType.class); + + cq.where(cb.like(cb.lower(root.get("name")), MatchMode.EXACT.toLowerCasePattern(name))); + + return session.createQuery(cq).uniqueResult(); } /** @@ -1521,18 +1667,21 @@ public ConceptMapType saveConceptMapType(ConceptMapType conceptMapType) throws D public void deleteConceptMapType(ConceptMapType conceptMapType) throws DAOException { sessionFactory.getCurrentSession().delete(conceptMapType); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTerms(boolean) */ - @SuppressWarnings("unchecked") @Override public List getConceptReferenceTerms(boolean includeRetired) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptReferenceTerm.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptReferenceTerm.class); + Root root = cq.from(ConceptReferenceTerm.class); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - return criteria.list(); + return session.createQuery(cq).getResultList(); } /** @@ -1540,68 +1689,84 @@ public List getConceptReferenceTerms(boolean includeRetire */ @Override public ConceptReferenceTerm getConceptReferenceTerm(Integer conceptReferenceTermId) throws DAOException { - return (ConceptReferenceTerm) sessionFactory.getCurrentSession().get(ConceptReferenceTerm.class, + return sessionFactory.getCurrentSession().get(ConceptReferenceTerm.class, conceptReferenceTermId); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermByUuid(java.lang.String) */ @Override public ConceptReferenceTerm getConceptReferenceTermByUuid(String uuid) throws DAOException { - return (ConceptReferenceTerm) sessionFactory.getCurrentSession().createQuery( - "from ConceptReferenceTerm crt where crt.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptReferenceTerm.class, uuid); } /** * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermsBySource(ConceptSource) */ - @SuppressWarnings("unchecked") @Override public List getConceptReferenceTermsBySource(ConceptSource conceptSource) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptReferenceTerm.class); - criteria.add(Restrictions.eq("conceptSource", conceptSource)); - return (List) criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptReferenceTerm.class); + Root root = cq.from(ConceptReferenceTerm.class); + + cq.where(cb.equal(root.get("conceptSource"), conceptSource)); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermByName(java.lang.String, * org.openmrs.ConceptSource) */ - @SuppressWarnings("rawtypes") @Override public ConceptReferenceTerm getConceptReferenceTermByName(String name, ConceptSource conceptSource) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptReferenceTerm.class); - criteria.add(Restrictions.ilike("name", name, MatchMode.EXACT)); - criteria.add(Restrictions.eq("conceptSource", conceptSource)); - List terms = criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptReferenceTerm.class); + Root root = cq.from(ConceptReferenceTerm.class); + + Predicate namePredicate = cb.like(cb.lower(root.get("name")), MatchMode.EXACT.toLowerCasePattern(name)); + Predicate sourcePredicate = cb.equal(root.get("conceptSource"), conceptSource); + + cq.where(cb.and(namePredicate, sourcePredicate)); + + List terms = session.createQuery(cq).getResultList(); if (terms.isEmpty()) { return null; } else if (terms.size() > 1) { - throw new APIException("ConceptReferenceTerm.foundMultipleTermsWithNameInSource", new Object[] { name, - conceptSource.getName() }); + throw new APIException("ConceptReferenceTerm.foundMultipleTermsWithNameInSource", + new Object[]{name, conceptSource.getName()}); } - return (ConceptReferenceTerm) terms.get(0); + return terms.get(0); } - + /** * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTermByCode(java.lang.String, * org.openmrs.ConceptSource) */ - @SuppressWarnings("rawtypes") @Override public ConceptReferenceTerm getConceptReferenceTermByCode(String code, ConceptSource conceptSource) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptReferenceTerm.class); - criteria.add(Restrictions.eq("code", code)); - criteria.add(Restrictions.eq("conceptSource", conceptSource)); - List terms = criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptReferenceTerm.class); + Root root = cq.from(ConceptReferenceTerm.class); + + Predicate codePredicate = cb.equal(root.get("code"), code); + Predicate sourcePredicate = cb.equal(root.get("conceptSource"), conceptSource); + + cq.where(cb.and(codePredicate, sourcePredicate)); + + List terms = session.createQuery(cq).getResultList(); + if (terms.isEmpty()) { return null; } else if (terms.size() > 1) { - throw new APIException("ConceptReferenceTerm.foundMultipleTermsWithCodeInSource", new Object[] { code, - conceptSource.getName() }); + throw new APIException("ConceptReferenceTerm.foundMultipleTermsWithCodeInSource", + new Object[] { code, conceptSource.getName() }); } - return (ConceptReferenceTerm) terms.get(0); + return terms.get(0); } /** @@ -1620,104 +1785,142 @@ public ConceptReferenceTerm saveConceptReferenceTerm(ConceptReferenceTerm concep public void deleteConceptReferenceTerm(ConceptReferenceTerm conceptReferenceTerm) throws DAOException { sessionFactory.getCurrentSession().delete(conceptReferenceTerm); } - - /** - * @see org.openmrs.api.db.ConceptDAO#getCountOfConceptReferenceTerms(String, ConceptSource, boolean) - */ + @Override public Long getCountOfConceptReferenceTerms(String query, ConceptSource conceptSource, boolean includeRetired) - throws DAOException { - Criteria criteria = createConceptReferenceTermCriteria(query, conceptSource, includeRetired); - - criteria.setProjection(Projections.rowCount()); - return (Long) criteria.uniqueResult(); + throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(ConceptReferenceTerm.class); + + List predicates = createConceptReferenceTermPredicates(cb, root, query, conceptSource, includeRetired); + + cq.where(predicates.toArray(new Predicate[]{})).select(cb.count(root)); + + return session.createQuery(cq).getSingleResult(); } - + + /** * @see org.openmrs.api.db.ConceptDAO#getConceptReferenceTerms(String, ConceptSource, Integer, * Integer, boolean) */ - @SuppressWarnings("unchecked") @Override public List getConceptReferenceTerms(String query, ConceptSource conceptSource, Integer start, - Integer length, boolean includeRetired) throws APIException { - Criteria criteria = createConceptReferenceTermCriteria(query, conceptSource, includeRetired); - + Integer length, boolean includeRetired) throws APIException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptReferenceTerm.class); + Root root = cq.from(ConceptReferenceTerm.class); + + List predicates = createConceptReferenceTermPredicates(cb, root, query, conceptSource, includeRetired); + cq.where(predicates.toArray(new Predicate[]{})); + + TypedQuery typedQuery = session.createQuery(cq); + if (start != null) { - criteria.setFirstResult(start); + typedQuery.setFirstResult(start); } if (length != null && length > 0) { - criteria.setMaxResults(length); + typedQuery.setMaxResults(length); } - - return criteria.list(); + + return typedQuery.getResultList(); } - - /** - * @param query - * @param includeRetired - * @return - */ - private Criteria createConceptReferenceTermCriteria(String query, ConceptSource conceptSource, boolean includeRetired) { - Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(ConceptReferenceTerm.class); + + private List createConceptReferenceTermPredicates(CriteriaBuilder cb, Root root, + String query, ConceptSource conceptSource, boolean includeRetired) { + List predicates = new ArrayList<>(); + if (conceptSource != null) { - searchCriteria.add(Restrictions.eq("conceptSource", conceptSource)); + predicates.add(cb.equal(root.get("conceptSource"), conceptSource)); } if (!includeRetired) { - searchCriteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } if (query != null) { - searchCriteria.add(Restrictions.or(Restrictions.ilike("name", query, MatchMode.ANYWHERE), Restrictions.ilike( - "code", query, MatchMode.ANYWHERE))); + Predicate namePredicate = cb.like(cb.lower(root.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(query)); + Predicate codePredicate = cb.like(cb.lower(root.get("code")), MatchMode.ANYWHERE.toLowerCasePattern(query)); + + predicates.add(cb.or(namePredicate, codePredicate)); } - return searchCriteria; + + return predicates; } - + /** * @see org.openmrs.api.db.ConceptDAO#getReferenceTermMappingsTo(ConceptReferenceTerm) */ - @SuppressWarnings("unchecked") @Override public List getReferenceTermMappingsTo(ConceptReferenceTerm term) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptReferenceTermMap.class); - criteria.add(Restrictions.eq("termB", term)); - return criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptReferenceTermMap.class); + Root root = cq.from(ConceptReferenceTermMap.class); + + cq.where(cb.equal(root.get("termB"), term)); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ConceptDAO#isConceptReferenceTermInUse(org.openmrs.ConceptReferenceTerm) */ @Override public boolean isConceptReferenceTermInUse(ConceptReferenceTerm term) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMap.class); - criteria.add(Restrictions.eq("conceptReferenceTerm", term)); - criteria.setProjection(Projections.rowCount()); - if ((Long) criteria.uniqueResult() > 0) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + + // Check in ConceptMap table + CriteriaQuery conceptMapQuery = cb.createQuery(Long.class); + Root conceptMapRoot = conceptMapQuery.from(ConceptMap.class); + conceptMapQuery.select(cb.count(conceptMapRoot)); + conceptMapQuery.where(cb.equal(conceptMapRoot.get("conceptReferenceTerm"), term)); + + Long conceptMapCount = session.createQuery(conceptMapQuery).uniqueResult(); + if (conceptMapCount > 0) { return true; } - - criteria = sessionFactory.getCurrentSession().createCriteria(ConceptReferenceTermMap.class); - criteria.add(Restrictions.eq("termB", term)); - criteria.setProjection(Projections.rowCount()); - return (Long) criteria.uniqueResult() > 0; + + // Check in ConceptReferenceTermMap table + CriteriaQuery conceptReferenceTermMapQuery = cb.createQuery(Long.class); + Root conceptReferenceTermMapRoot = + conceptReferenceTermMapQuery.from(ConceptReferenceTermMap.class); + conceptReferenceTermMapQuery.select(cb.count(conceptReferenceTermMapRoot)); + conceptReferenceTermMapQuery.where(cb.equal(conceptReferenceTermMapRoot.get("termB"), term)); + + Long conceptReferenceTermMapCount = session.createQuery(conceptReferenceTermMapQuery).uniqueResult(); + return conceptReferenceTermMapCount > 0; } - + /** * @see org.openmrs.api.db.ConceptDAO#isConceptMapTypeInUse(org.openmrs.ConceptMapType) */ @Override public boolean isConceptMapTypeInUse(ConceptMapType mapType) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMap.class); - criteria.add(Restrictions.eq("conceptMapType", mapType)); - criteria.setProjection(Projections.rowCount()); - if ((Long) criteria.uniqueResult() > 0) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + + // Check in ConceptMap table + CriteriaQuery conceptQuery = cb.createQuery(Long.class); + Root conceptRoot = conceptQuery.from(ConceptMap.class); + conceptQuery.select(cb.count(conceptRoot)); + conceptQuery.where(cb.equal(conceptRoot.get("conceptMapType"), mapType)); + + Long conceptCount = session.createQuery(conceptQuery).uniqueResult(); + if (conceptCount > 0) { return true; } - - criteria = sessionFactory.getCurrentSession().createCriteria(ConceptReferenceTermMap.class); - criteria.add(Restrictions.eq("conceptMapType", mapType)); - criteria.setProjection(Projections.rowCount()); - return (Long) criteria.uniqueResult() > 0; + + // Check in ConceptReferenceTermMap table + CriteriaQuery conceptReferenceTermMapQuery = cb.createQuery(Long.class); + Root conceptReferenceTermMapRoot = conceptReferenceTermMapQuery.from(ConceptReferenceTermMap.class); + conceptReferenceTermMapQuery.select(cb.count(conceptReferenceTermMapRoot)); + conceptReferenceTermMapQuery.where(cb.equal(conceptReferenceTermMapRoot.get("conceptMapType"), mapType)); + + Long conceptReferenceTermMapCount = session.createQuery(conceptReferenceTermMapQuery).uniqueResult(); + return conceptReferenceTermMapCount > 0; } /** @@ -1749,25 +1952,28 @@ public List getConceptsByName(final String name, final Locale locale, f */ @Override public Concept getConceptByName(final String name) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptName.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptName.class); + Root root = cq.from(ConceptName.class); + Join conceptJoin = root.join("concept"); + Locale locale = Context.getLocale(); Locale language = new Locale(locale.getLanguage() + "%"); - criteria.add(Restrictions.or(Restrictions.eq("locale", locale), Restrictions.like("locale", language))); - + List predicates = new ArrayList<>(); + + predicates.add(cb.or(cb.equal(root.get("locale"), locale), cb.like(root.get("locale").as(String.class), language.toString()))); if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) { - criteria.add(Restrictions.ilike("name", name)); + predicates.add(cb.like(cb.lower(root.get("name")), name.toLowerCase())); } else { - criteria.add(Restrictions.eq("name", name)); + predicates.add(cb.equal(root.get("name"), name)); } - - criteria.add(Restrictions.eq("voided", false)); - - criteria.createAlias("concept", "concept"); - criteria.add(Restrictions.eq("concept.retired", false)); - - @SuppressWarnings("unchecked") - List list = criteria.list(); + predicates.add(cb.isFalse(root.get("voided"))); + predicates.add(cb.isFalse(conceptJoin.get("retired"))); + + cq.where(predicates.toArray(new Predicate[0])); + + List list = session.createQuery(cq).getResultList(); LinkedHashSet concepts = transformNamesToConcepts(list); if (concepts.size() == 1) { @@ -1776,7 +1982,7 @@ public Concept getConceptByName(final String name) { log.warn("No concept found for '" + name + "'"); } else { log.warn("Multiple concepts found for '" + name + "'"); - + for (Concept concept : concepts) { for (ConceptName conceptName : concept.getNames(locale)) { if (conceptName.getName().equalsIgnoreCase(name)) { @@ -1790,7 +1996,7 @@ public Concept getConceptByName(final String name) { } } } - + return null; } @@ -1822,7 +2028,7 @@ public ConceptMapType getDefaultConceptMapType() throws DAOException { sessionFactory.getCurrentSession().setHibernateFlushMode(previousFlushMode); } } - + /** * @see org.openmrs.api.db.ConceptDAO#isConceptNameDuplicate(org.openmrs.ConceptName) */ @@ -1843,20 +2049,28 @@ public boolean isConceptNameDuplicate(ConceptName name) { return false; } } - - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptName.class); - - criteria.add(Restrictions.eq("voided", false)); - criteria.add(Restrictions.or(Restrictions.eq("locale", name.getLocale()), Restrictions.eq("locale", new Locale(name - .getLocale().getLanguage())))); + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptName.class); + Root root = cq.from(ConceptName.class); + + List predicates = new ArrayList<>(); + + predicates.add(cb.isFalse(root.get("voided"))); + predicates.add(cb.or(cb.equal(root.get("locale"), name.getLocale()), + cb.equal(root.get("locale"), new Locale(name.getLocale().getLanguage())))); + if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) { - criteria.add(Restrictions.eq("name", name.getName()).ignoreCase()); + predicates.add(cb.equal(cb.lower(root.get("name")), name.getName().toLowerCase())); } else { - criteria.add(Restrictions.eq("name", name.getName())); + predicates.add(cb.equal(root.get("name"), name.getName())); } - - List candidateNames = criteria.list(); - + + cq.where(predicates.toArray(new Predicate[0])); + + List candidateNames = session.createQuery(cq).getResultList(); + for (ConceptName candidateName : candidateNames) { if (candidateName.getConcept().getRetired()) { continue; @@ -1864,13 +2078,12 @@ public boolean isConceptNameDuplicate(ConceptName name) { if (candidateName.getConcept().equals(name.getConcept())) { continue; } - - //If it is a default name for a concept + // If it is a default name for a concept if (candidateName.getConcept().getName(candidateName.getLocale()).equals(candidateName)) { return true; } } - + return false; } @@ -1883,47 +2096,70 @@ public List getDrugs(String searchPhrase, Locale locale, boolean exactLoca return drugQuery.list(); } - + /** - * @see org.openmrs.api.db.ConceptDAO#getDrugsByMapping(String, ConceptSource, Collection, - * boolean) + * @see org.openmrs.api.db.ConceptDAO#getDrugsByMapping(String, ConceptSource, Collection, boolean) */ @Override public List getDrugsByMapping(String code, ConceptSource conceptSource, - Collection withAnyOfTheseTypes, boolean includeRetired) throws DAOException { - - Criteria criteria = createSearchDrugByMappingCriteria(code, conceptSource, includeRetired); - // match with any of the supplied collection of conceptMapTypes + Collection withAnyOfTheseTypes, boolean includeRetired) throws DAOException { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Drug.class); + Root drugRoot = cq.from(Drug.class); + + Join drugReferenceMapJoin = drugRoot.join("drugReferenceMaps"); + Join termJoin = drugReferenceMapJoin.join("conceptReferenceTerm"); + List basePredicates = createSearchDrugByMappingPredicates(cb, drugRoot, drugReferenceMapJoin, termJoin, code, conceptSource, includeRetired); + if (!withAnyOfTheseTypes.isEmpty()) { - criteria.add(Restrictions.in("map.conceptMapType", withAnyOfTheseTypes)); + // Create a predicate to check if the ConceptMapType is in the provided collection + Predicate mapTypePredicate = drugReferenceMapJoin.get("conceptMapType").in(withAnyOfTheseTypes); + basePredicates.add(mapTypePredicate); } - //check whether retired on not retired drugs - return (List) criteria.list(); + + cq.where(basePredicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList().stream().distinct().collect(Collectors.toList()); } - + /** * @see org.openmrs.api.db.ConceptDAO#getDrugs */ @Override public Drug getDrugByMapping(String code, ConceptSource conceptSource, - Collection withAnyOfTheseTypesOrOrderOfPreference) throws DAOException { - Criteria criteria = createSearchDrugByMappingCriteria(code, conceptSource, true); - - // match with any of the supplied collection or order of preference of conceptMapTypes + Collection withAnyOfTheseTypesOrOrderOfPreference) throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Drug.class); + Root drugRoot = cq.from(Drug.class); + + Join drugReferenceMapJoin = drugRoot.join("drugReferenceMaps"); + Join termJoin = drugReferenceMapJoin.join("conceptReferenceTerm"); + + List basePredicates = createSearchDrugByMappingPredicates(cb, drugRoot, drugReferenceMapJoin, termJoin, code, conceptSource, true); + if (!withAnyOfTheseTypesOrOrderOfPreference.isEmpty()) { for (ConceptMapType conceptMapType : withAnyOfTheseTypesOrOrderOfPreference) { - criteria.add(Restrictions.eq("map.conceptMapType", conceptMapType)); - List drugs = criteria.list(); + + List predicates = new ArrayList<>(basePredicates); + predicates.add(cb.equal(drugReferenceMapJoin.get("conceptMapType"), conceptMapType)); + cq.where(predicates.toArray(new Predicate[]{})); + + TypedQuery query = session.createQuery(cq); + List drugs = query.getResultList(); if (drugs.size() > 1) { throw new DAOException("There are multiple matches for the highest-priority ConceptMapType"); } else if (drugs.size() == 1) { return drugs.get(0); } - //reset for the next execution to avoid unwanted AND clauses on every found map type - criteria = createSearchDrugByMappingCriteria(code, conceptSource, true); } } else { - List drugs = criteria.list(); + cq.where(basePredicates.toArray(new Predicate[]{})); + + TypedQuery query = session.createQuery(cq); + List drugs = query.getResultList(); if (drugs.size() > 1) { throw new DAOException("There are multiple matches for the highest-priority ConceptMapType"); } else if (drugs.size() == 1) { @@ -1933,13 +2169,18 @@ public Drug getDrugByMapping(String code, ConceptSource conceptSource, return null; } + /** * @see ConceptDAO#getAllConceptAttributeTypes() */ - @SuppressWarnings("unchecked") @Override public List getAllConceptAttributeTypes() { - return sessionFactory.getCurrentSession().createCriteria(ConceptAttributeType.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptAttributeType.class); + cq.from(ConceptAttributeType.class); + + return session.createQuery(cq).getResultList(); } /** @@ -1956,7 +2197,7 @@ public ConceptAttributeType saveConceptAttributeType(ConceptAttributeType concep */ @Override public ConceptAttributeType getConceptAttributeType(Integer id) { - return (ConceptAttributeType) sessionFactory.getCurrentSession().get(ConceptAttributeType.class, id); + return sessionFactory.getCurrentSession().get(ConceptAttributeType.class, id); } /** @@ -1964,8 +2205,7 @@ public ConceptAttributeType getConceptAttributeType(Integer id) { */ @Override public ConceptAttributeType getConceptAttributeTypeByUuid(String uuid) { - return (ConceptAttributeType) sessionFactory.getCurrentSession().createCriteria(ConceptAttributeType.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAttributeType.class, uuid); } /** @@ -1981,13 +2221,17 @@ public void deleteConceptAttributeType(ConceptAttributeType conceptAttributeType */ @Override public List getConceptAttributeTypes(String name) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptAttributeType.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptAttributeType.class); + Root root = cq.from(ConceptAttributeType.class); //match name anywhere and case insensitive if (name != null) { - criteria.add(Restrictions.ilike("name", name, MatchMode.ANYWHERE)); + cq.where(cb.like(cb.lower(root.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(name))); } - return criteria.list(); + + return session.createQuery(cq).getResultList(); } /** @@ -1995,9 +2239,14 @@ public List getConceptAttributeTypes(String name) { */ @Override public ConceptAttributeType getConceptAttributeTypeByName(String exactName) { - return (ConceptAttributeType) sessionFactory.getCurrentSession().createCriteria(ConceptAttributeType.class).add( - Restrictions.eq("name", exactName)).uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptAttributeType.class); + Root root = cq.from(ConceptAttributeType.class); + cq.where(cb.equal(root.get("name"), exactName)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -2005,8 +2254,7 @@ public ConceptAttributeType getConceptAttributeTypeByName(String exactName) { */ @Override public ConceptAttribute getConceptAttributeByUuid(String uuid) { - return (ConceptAttribute) sessionFactory.getCurrentSession().createCriteria(ConceptAttribute.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptAttribute.class, uuid); } /** @@ -2014,80 +2262,75 @@ public ConceptAttribute getConceptAttributeByUuid(String uuid) { */ @Override public long getConceptAttributeCount(ConceptAttributeType conceptAttributeType) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptAttribute.class); - criteria.add(Restrictions.eq("attributeType", conceptAttributeType)); - criteria.setProjection(Projections.rowCount()); - return (Long) criteria.list().get(0); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(ConceptAttribute.class); + + cq.select(cb.count(root)).where(cb.equal(root.get("attributeType"), conceptAttributeType)); + return session.createQuery(cq).getSingleResult(); } @Override public List getConceptsByClass(ConceptClass conceptClass) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Concept.class); - return criteria.add(Restrictions.eq("conceptClass", conceptClass)).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Concept.class); + Root root = cq.from(Concept.class); + + cq.where(cb.equal(root.get("conceptClass"), conceptClass)); + + return session.createQuery(cq).getResultList(); } - - private Criteria createSearchDrugByMappingCriteria(String code, ConceptSource conceptSource, boolean includeRetired) { - Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug"); - searchCriteria.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE); + + private List createSearchDrugByMappingPredicates(CriteriaBuilder cb, Root drugRoot, Join drugReferenceMapJoin, + Join termJoin, + String code, ConceptSource conceptSource, boolean includeRetired) { + List predicates = new ArrayList<>(); - //join to the drugReferenceMap table - searchCriteria.createAlias("drug.drugReferenceMaps", "map"); - if (code != null || conceptSource != null) { - // join to the conceptReferenceTerm table - searchCriteria.createAlias("map.conceptReferenceTerm", "term"); - } - // match the source code to the passed code if (code != null) { - searchCriteria.add(Restrictions.eq("term.code", code)); + predicates.add(cb.equal(termJoin.get("code"), code)); } - // match the conceptSource to the passed in concept source, null accepted if (conceptSource != null) { - searchCriteria.add(Restrictions.eq("term.conceptSource", conceptSource)); + predicates.add(cb.equal(termJoin.get("conceptSource"), conceptSource)); } - //check whether retired or not retired drugs if (!includeRetired) { - searchCriteria.add(Restrictions.eq("drug.retired", false)); + predicates.add(cb.isFalse(drugRoot.get("retired"))); } - return searchCriteria; - } - private Criteria createSearchConceptMapCriteria(String code, String sourceName, boolean includeRetired) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMap.class); + return predicates; + } - //join to the conceptReferenceTerm table - criteria.createAlias("conceptReferenceTerm", "term"); + private List createSearchConceptMapCriteria(CriteriaBuilder cb, Root root, String code, String sourceName, boolean includeRetired) { + List predicates = new ArrayList<>(); - // match the source code to the passed code - if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) { - criteria.add(Restrictions.eq("term.code", code).ignoreCase()); - } else { - criteria.add(Restrictions.eq("term.code", code)); - } + Join termJoin = root.join("conceptReferenceTerm"); - // join to concept reference source and match to the h17Code or source name - criteria.createAlias("term.conceptSource", "source"); + // Match the source code to the passed code if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) { - criteria.add(Restrictions.or(Restrictions.eq("source.name", sourceName).ignoreCase(), Restrictions.eq( - "source.hl7Code", sourceName).ignoreCase())); + predicates.add(cb.equal(cb.lower(termJoin.get("code")), code.toLowerCase())); } else { - criteria.add(Restrictions.or(Restrictions.eq("source.name", sourceName), Restrictions.eq("source.hl7Code", - sourceName))); + predicates.add(cb.equal(termJoin.get("code"), code)); } - criteria.createAlias("concept", "concept"); + // Join to concept reference source and match to the hl7Code or source name + Join sourceJoin = termJoin.join("conceptSource"); + + Predicate namePredicate = Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive() ? + cb.equal(cb.lower(sourceJoin.get("name")), sourceName.toLowerCase()) : + cb.equal(sourceJoin.get("name"), sourceName); + Predicate hl7CodePredicate = Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive() ? + cb.equal(cb.lower(sourceJoin.get("hl7Code")), sourceName.toLowerCase()) : + cb.equal(sourceJoin.get("hl7Code"), sourceName); + + predicates.add(cb.or(namePredicate, hl7CodePredicate)); + // Join to concept and filter retired ones if necessary + Join conceptJoin = root.join("concept"); if (!includeRetired) { - // ignore retired concepts - criteria.add(Restrictions.eq("concept.retired", false)); - } else { - // sort retired concepts to the end of the list - criteria.addOrder(Order.asc("concept.retired")); + predicates.add(cb.isFalse(conceptJoin.get("retired"))); } - - // we only want distinct concepts - criteria.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE); - - return criteria; + return predicates; } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConditionDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConditionDAO.java index a9c7307016db..ae81c9c1bd83 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConditionDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateConditionDAO.java @@ -9,9 +9,10 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.TypedQuery; +import java.util.Arrays; import java.util.List; -import org.hibernate.query.Query; import org.hibernate.SessionFactory; import org.openmrs.Condition; import org.openmrs.Encounter; @@ -20,6 +21,10 @@ import org.openmrs.api.db.ConditionDAO; import org.openmrs.api.db.DAOException; +import static org.openmrs.ConditionClinicalStatus.ACTIVE; +import static org.openmrs.ConditionClinicalStatus.RECURRENCE; +import static org.openmrs.ConditionClinicalStatus.RELAPSE; + /** * Hibernate implementation of the ConditionDAO * @@ -60,8 +65,7 @@ public Condition getCondition(Integer conditionId) { */ @Override public Condition getConditionByUuid(String uuid) { - return sessionFactory.getCurrentSession().createQuery("from Condition c where c.uuid = :uuid", Condition.class) - .setParameter("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Condition.class, uuid); } /** @@ -69,11 +73,11 @@ public Condition getConditionByUuid(String uuid) { */ @Override public List getConditionsByEncounter(Encounter encounter) throws APIException { - Query query = sessionFactory.getCurrentSession().createQuery( + TypedQuery query = sessionFactory.getCurrentSession().createQuery( "from Condition c where c.encounter.encounterId = :encounterId and c.voided = false order " + "by c.dateCreated desc", Condition.class); query.setParameter("encounterId", encounter.getId()); - return query.list(); + return query.getResultList(); } /** @@ -84,11 +88,15 @@ public List getConditionsByEncounter(Encounter encounter) throws APIE */ @Override public List getActiveConditions(Patient patient) { - Query query = sessionFactory.getCurrentSession().createQuery( - "from Condition c where c.patient.patientId = :patientId and c.clinicalStatus = 'ACTIVE' and c.voided = false order " - + "by c.dateCreated desc", Condition.class); + TypedQuery query = sessionFactory.getCurrentSession().createQuery( + "from Condition c " + + "where c.patient.patientId = :patientId " + + "and c.clinicalStatus in :activeStatuses " + + "and c.voided = false " + + "order by c.dateCreated desc", Condition.class); query.setParameter("patientId", patient.getId()); - return query.list(); + query.setParameter("activeStatuses", Arrays.asList(ACTIVE, RECURRENCE, RELAPSE)); + return query.getResultList(); } /** @@ -96,11 +104,13 @@ public List getActiveConditions(Patient patient) { */ @Override public List getAllConditions(Patient patient) { - Query query = sessionFactory.getCurrentSession().createQuery( - "from Condition con where con.patient.patientId = :patientId " + - "order by con.dateCreated desc", Condition.class); + TypedQuery query = sessionFactory.getCurrentSession().createQuery( + "from Condition c " + + "where c.patient.patientId = :patientId " + + "and c.voided = false " + + "order by c.dateCreated desc", Condition.class); query.setParameter("patientId", patient.getId()); - return query.list(); + return query.getResultList(); } /** diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateContextDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateContextDAO.java index c6b8c109b5c9..6aa3bfd17224 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateContextDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateContextDAO.java @@ -9,11 +9,21 @@ */ package org.openmrs.api.db.hibernate; +import java.io.File; +import java.net.URL; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + import org.apache.commons.lang3.StringUtils; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; -import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -40,16 +50,6 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronizationManager; -import java.io.File; -import java.net.URL; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Future; - /** * Hibernate specific implementation of the {@link ContextDAO}. These methods should not be used * directly, instead, the methods on the static {@link Context} file should be used. @@ -61,6 +61,8 @@ public class HibernateContextDAO implements ContextDAO { private static final Logger log = LoggerFactory.getLogger(HibernateContextDAO.class); + private static final Long DEFAULT_UNLOCK_ACCOUNT_WAITING_TIME = TimeUnit.MILLISECONDS.convert(5L, TimeUnit.MINUTES); + /** * Hibernate session factory */ @@ -137,9 +139,22 @@ public User authenticate(String login, String password) throws ContextAuthentica // if they've been locked out, don't continue with the authentication if (lockoutTime > 0) { - // unlock them after 5 mins, otherwise reset the timestamp - // to now and make them wait another 5 mins - if (System.currentTimeMillis() - lockoutTime > 300000) { + float userUnlockWaitTime = 300000; + + //Set the value of user lockout time as defined in the Global properties + //Note that in the Global properties, the user lockout time is defined in minutes + try { + userUnlockWaitTime = Math.abs(Float.parseFloat(Context.getAdministrationService() + .getGlobalProperty(OpenmrsConstants.GP_USER_UNLOCK_WAIT_TIME).trim())) * 60000; + } + catch (Exception ex) { + log.error("Unable to convert the global property {} to a valid number. Using default value of 5 mins.", + OpenmrsConstants.GP_USER_UNLOCK_WAIT_TIME); + } + + // unlock them after a time defined by userUnlockWaitTime in milliseconds, otherwise reset the timestamp + // to now and make them wait for another period of time defined by userUnlockWaitTime in milliseconds + if (System.currentTimeMillis() - lockoutTime > userUnlockWaitTime) { candidateUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_LOGIN_ATTEMPTS, "0"); candidateUser.removeUserProperty(OpenmrsConstants.USER_PROPERTY_LOCKOUT_TIMESTAMP); saveUserProperties(candidateUser); @@ -212,6 +227,28 @@ public User authenticate(String login, String password) throws ContextAuthentica throw new ContextAuthenticationException(errorMsg); } + private Long getUnlockTimeMs() { + String unlockTimeGPValue = Context.getAdministrationService().getGlobalProperty( + OpenmrsConstants.GP_UNLOCK_ACCOUNT_WAITING_TIME); + if (StringUtils.isNotBlank(unlockTimeGPValue)) { + return convertUnlockAccountWaitingTimeGP(unlockTimeGPValue); + } + else { + return DEFAULT_UNLOCK_ACCOUNT_WAITING_TIME; + } + } + + private Long convertUnlockAccountWaitingTimeGP(String waitingTime) { + try { + return TimeUnit.MILLISECONDS.convert(Long.valueOf(waitingTime), TimeUnit.MINUTES); + } catch (Exception ex) { + log.error("Unable to convert the global property " + + OpenmrsConstants.GP_UNLOCK_ACCOUNT_WAITING_TIME + + "to a valid Long. Using default value of 5"); + return DEFAULT_UNLOCK_ACCOUNT_WAITING_TIME; + } + } + /** * @see org.openmrs.api.db.ContextDAO#getUserByUuid(java.lang.String) */ @@ -223,8 +260,7 @@ public User getUserByUuid(String uuid) { FlushMode flushMode = sessionFactory.getCurrentSession().getHibernateFlushMode(); sessionFactory.getCurrentSession().setHibernateFlushMode(FlushMode.MANUAL); - User u = (User) sessionFactory.getCurrentSession().createQuery("from User u where u.uuid = :uuid").setString("uuid", - uuid).uniqueResult(); + User u = HibernateUtil.getUniqueEntityByUUID(sessionFactory, User.class, uuid); // reset the flush mode to whatever it was before sessionFactory.getCurrentSession().setHibernateFlushMode(flushMode); @@ -469,21 +505,25 @@ public void updateSearchIndexForType(Class type) { session.setCacheMode(CacheMode.IGNORE); //Scrollable results will avoid loading too many objects in memory - ScrollableResults results = session.createCriteria(type).setFetchSize(1000).scroll(ScrollMode.FORWARD_ONLY); - int index = 0; - while (results.next()) { - index++; - //index each element - session.index(results.get(0)); - if (index % 1000 == 0) { - //apply changes to indexes - session.flushToIndexes(); - //free memory since the queue is processed - session.clear(); + try (ScrollableResults results = HibernateUtil.getScrollableResult(sessionFactory, type, 1000)) { + int index = 0; + while (results.next()) { + index++; + //index each element + session.index(results.get(0)); + if (index % 1000 == 0) { + //apply changes to indexes + session.flushToIndexes(); + //free memory since the queue is processed + session.clear(); + // reset index to avoid overflows + index = 0; + } } + } finally { + session.flushToIndexes(); + session.clear(); } - session.flushToIndexes(); - session.clear(); } finally { session.setHibernateFlushMode(flushMode); @@ -553,6 +593,7 @@ public Future updateSearchIndexAsync() { /** * @see ContextDAO#getDatabaseConnection() */ + @Override public Connection getDatabaseConnection() { try { return SessionFactoryUtils.getDataSource(sessionFactory).getConnection(); diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateDatatypeDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateDatatypeDAO.java index 71a3e53f4c11..37ee0340438f 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateDatatypeDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateDatatypeDAO.java @@ -11,7 +11,6 @@ import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Restrictions; import org.openmrs.api.db.ClobDatatypeStorage; import org.openmrs.api.db.DatatypeDAO; @@ -49,7 +48,7 @@ private Session session() { */ @Override public ClobDatatypeStorage getClobDatatypeStorage(Integer id) { - return (ClobDatatypeStorage) session().get(ClobDatatypeStorage.class, id); + return session().get(ClobDatatypeStorage.class, id); } /** @@ -57,8 +56,7 @@ public ClobDatatypeStorage getClobDatatypeStorage(Integer id) { */ @Override public ClobDatatypeStorage getClobDatatypeStorageByUuid(String uuid) { - return (ClobDatatypeStorage) session().createCriteria(ClobDatatypeStorage.class).add(Restrictions.eq("uuid", uuid)) - .uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ClobDatatypeStorage.class, uuid); } /** diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateDiagnosisDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateDiagnosisDAO.java index 6c8dac7f919e..324ef036c46e 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateDiagnosisDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateDiagnosisDAO.java @@ -12,9 +12,8 @@ import java.util.Date; import java.util.List; -import org.hibernate.Query; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Restrictions; import org.openmrs.ConditionVerificationStatus; import org.openmrs.Diagnosis; import org.openmrs.DiagnosisAttribute; @@ -26,7 +25,10 @@ import org.openmrs.api.db.DiagnosisDAO; import org.springframework.transaction.annotation.Transactional; +import javax.persistence.Query; import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; /** @@ -86,11 +88,11 @@ public List getActiveDiagnoses(Patient patient, Date fromDate) { "from Diagnosis d where d.patient.patientId = :patientId and d.voided = false " + fromDateCriteria + " order by d.dateCreated desc"); - query.setInteger("patientId", patient.getId()); + query.setParameter("patientId", patient.getId()); if(fromDate != null){ - query.setDate("fromDate", fromDate); + query.setParameter("fromDate", fromDate); } - return query.list(); + return query.getResultList(); } /** @@ -151,7 +153,7 @@ public List getDiagnosesByVisit(Visit visit, boolean primaryOnly, boo */ @Override public Diagnosis getDiagnosisById(Integer diagnosisId) { - return (Diagnosis) sessionFactory.getCurrentSession().get(Diagnosis.class, diagnosisId); + return sessionFactory.getCurrentSession().get(Diagnosis.class, diagnosisId); } /** @@ -162,8 +164,7 @@ public Diagnosis getDiagnosisById(Integer diagnosisId) { */ @Override public Diagnosis getDiagnosisByUuid(String uuid){ - return (Diagnosis) sessionFactory.getCurrentSession().createQuery("from Diagnosis d where d.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Diagnosis.class, uuid); } /** @@ -178,11 +179,15 @@ public void deleteDiagnosis(Diagnosis diagnosis) throws DAOException{ /** * @see org.openmrs.api.db.DiagnosisDAO#getAllDiagnosisAttributeTypes() */ - @SuppressWarnings("unchecked") @Override @Transactional(readOnly = true) public List getAllDiagnosisAttributeTypes() throws DAOException { - return sessionFactory.getCurrentSession().createCriteria(DiagnosisAttributeType.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(DiagnosisAttributeType.class); + cq.from(DiagnosisAttributeType.class); + + return session.createQuery(cq).getResultList(); } /** @@ -200,8 +205,7 @@ public DiagnosisAttributeType getDiagnosisAttributeTypeById(Integer id) throws D @Override @Transactional(readOnly = true) public DiagnosisAttributeType getDiagnosisAttributeTypeByUuid(String uuid) throws DAOException { - return (DiagnosisAttributeType) sessionFactory.getCurrentSession().createCriteria(DiagnosisAttributeType.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, DiagnosisAttributeType.class, uuid); } /** @@ -229,7 +233,6 @@ public void deleteDiagnosisAttributeType(DiagnosisAttributeType diagnosisAttribu @Override @Transactional(readOnly = true) public DiagnosisAttribute getDiagnosisAttributeByUuid(String uuid) throws DAOException { - return (DiagnosisAttribute) sessionFactory.getCurrentSession().createCriteria(DiagnosisAttribute.class).add(Restrictions.eq("uuid", uuid)) - .uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, DiagnosisAttribute.class, uuid); } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateEncounterDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateEncounterDAO.java index 9e257415bc88..407d3f727329 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateEncounterDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateEncounterDAO.java @@ -9,33 +9,44 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.CacheRetrieveMode; +import javax.persistence.CacheStoreMode; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import liquibase.pro.packaged.Q; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; import org.hibernate.FlushMode; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Conjunction; -import org.hibernate.criterion.Disjunction; -import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Order; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Restrictions; -import org.hibernate.sql.JoinType; import org.openmrs.Cohort; import org.openmrs.Encounter; +import org.openmrs.EncounterProvider; import org.openmrs.EncounterRole; import org.openmrs.EncounterType; +import org.openmrs.Form; import org.openmrs.Location; import org.openmrs.Patient; +import org.openmrs.Person; +import org.openmrs.PersonName; +import org.openmrs.Provider; import org.openmrs.Visit; +import org.openmrs.VisitType; import org.openmrs.api.EncounterService; import org.openmrs.api.context.Context; import org.openmrs.api.db.DAOException; @@ -87,75 +98,87 @@ public void deleteEncounter(Encounter encounter) throws DAOException { */ @Override public Encounter getEncounter(Integer encounterId) throws DAOException { - return (Encounter) sessionFactory.getCurrentSession().get(Encounter.class, encounterId); + return sessionFactory.getCurrentSession().get(Encounter.class, encounterId); } - + /** * @see org.openmrs.api.db.EncounterDAO#getEncountersByPatientId(java.lang.Integer) */ @Override - @SuppressWarnings("unchecked") public List getEncountersByPatientId(Integer patientId) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Encounter.class).createAlias("patient", "p").add( - Restrictions.eq("p.patientId", patientId)).add(Restrictions.eq("voided", false)).addOrder( - Order.desc("encounterDatetime")); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Encounter.class); + Root encounterRoot = cq.from(Encounter.class); + + Join patientJoin = encounterRoot.join("patient"); + + cq.select(encounterRoot).where( + cb.equal(patientJoin.get("patientId"), patientId), + cb.isFalse(encounterRoot.get("voided")) + ).orderBy(cb.desc(encounterRoot.get("encounterDatetime"))); - return crit.list(); + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.EncounterDAO#getEncounters(org.openmrs.parameter.EncounterSearchCriteria) */ - @SuppressWarnings("unchecked") @Override public List getEncounters(EncounterSearchCriteria searchCriteria) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Encounter.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Encounter.class); + Root encounter = cq.from(Encounter.class); + + List predicates = new ArrayList<>(); + if (searchCriteria.getPatient() != null && searchCriteria.getPatient().getPatientId() != null) { - crit.add(Restrictions.eq("patient", searchCriteria.getPatient())); + predicates.add(cb.equal(encounter.get("patient"), searchCriteria.getPatient())); } if (searchCriteria.getLocation() != null && searchCriteria.getLocation().getLocationId() != null) { - crit.add(Restrictions.eq("location", searchCriteria.getLocation())); + predicates.add(cb.equal(encounter.get("location"), searchCriteria.getLocation())); } if (searchCriteria.getFromDate() != null) { - crit.add(Restrictions.ge("encounterDatetime", searchCriteria.getFromDate())); + predicates.add(cb.greaterThanOrEqualTo(encounter.get("encounterDatetime"), searchCriteria.getFromDate())); } if (searchCriteria.getToDate() != null) { - crit.add(Restrictions.le("encounterDatetime", searchCriteria.getToDate())); + predicates.add(cb.lessThanOrEqualTo(encounter.get("encounterDatetime"), searchCriteria.getToDate())); } if (searchCriteria.getDateChanged() != null) { - crit.add( - Restrictions.or( - Restrictions.and( - Restrictions.isNull("dateChanged"), - Restrictions.ge("dateCreated", searchCriteria.getDateChanged()) - ), - Restrictions.ge("dateChanged", searchCriteria.getDateChanged()) - ) - ); + predicates.add(cb.or( + cb.and( + cb.isNull(encounter.get("dateChanged")), + cb.greaterThanOrEqualTo(encounter.get("dateCreated"), searchCriteria.getDateChanged()) + ), + cb.greaterThanOrEqualTo(encounter.get("dateChanged"), searchCriteria.getDateChanged()) + )); } if (searchCriteria.getEnteredViaForms() != null && !searchCriteria.getEnteredViaForms().isEmpty()) { - crit.add(Restrictions.in("form", searchCriteria.getEnteredViaForms())); + predicates.add(encounter.get("form").in(searchCriteria.getEnteredViaForms())); } if (searchCriteria.getEncounterTypes() != null && !searchCriteria.getEncounterTypes().isEmpty()) { - crit.add(Restrictions.in("encounterType", searchCriteria.getEncounterTypes())); + predicates.add(encounter.get("encounterType").in(searchCriteria.getEncounterTypes())); } if (searchCriteria.getProviders() != null && !searchCriteria.getProviders().isEmpty()) { - crit.createAlias("encounterProviders", "ep"); - crit.add(Restrictions.in("ep.provider", searchCriteria.getProviders())); + Join encounterProvider = encounter.join("encounterProviders"); + predicates.add(encounterProvider.get("provider").in(searchCriteria.getProviders())); } if (searchCriteria.getVisitTypes() != null && !searchCriteria.getVisitTypes().isEmpty()) { - crit.createAlias("visit", "v"); - crit.add(Restrictions.in("v.visitType", searchCriteria.getVisitTypes())); + Join visit = encounter.join("visit"); + predicates.add(visit.get("visitType").in(searchCriteria.getVisitTypes())); } if (searchCriteria.getVisits() != null && !searchCriteria.getVisits().isEmpty()) { - crit.add(Restrictions.in("visit", searchCriteria.getVisits())); + predicates.add(encounter.get("visit").in(searchCriteria.getVisits())); } if (!searchCriteria.getIncludeVoided()) { - crit.add(Restrictions.eq("voided", false)); + predicates.add(cb.isFalse(encounter.get("voided"))); } - crit.addOrder(Order.asc("encounterDatetime")); - return crit.list(); + + cq.select(encounter).where(predicates.toArray(new Predicate[]{})) + .orderBy(cb.asc(encounter.get("encounterDatetime"))); + + return session.createQuery(cq).getResultList(); } /** @@ -180,49 +203,59 @@ public void deleteEncounterType(EncounterType encounterType) throws DAOException */ @Override public EncounterType getEncounterType(Integer encounterTypeId) throws DAOException { - return (EncounterType) sessionFactory.getCurrentSession().get(EncounterType.class, encounterTypeId); + return sessionFactory.getCurrentSession().get(EncounterType.class, encounterTypeId); } - + /** * @see org.openmrs.api.EncounterService#getEncounterType(java.lang.String) */ @Override public EncounterType getEncounterType(String name) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(EncounterType.class); - crit.add(Restrictions.eq("retired", false)); - crit.add(Restrictions.eq("name", name)); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(EncounterType.class); + Root root = cq.from(EncounterType.class); + + cq.where(cb.isFalse(root.get("retired")), cb.equal(root.get("name"), name)); - return (EncounterType) crit.uniqueResult(); + return session.createQuery(cq).uniqueResult(); } - + /** * @see org.openmrs.api.db.EncounterDAO#getAllEncounterTypes(java.lang.Boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllEncounterTypes(Boolean includeRetired) throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(EncounterType.class); + Root root = cq.from(EncounterType.class); - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(EncounterType.class); - - criteria.addOrder(Order.asc("name")); - + cq.orderBy(cb.asc(root.get("name"))); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - return criteria.list(); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.EncounterDAO#findEncounterTypes(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List findEncounterTypes(String name) throws DAOException { - return sessionFactory.getCurrentSession().createCriteria(EncounterType.class) - // 'ilike' case insensitive search - .add(Restrictions.ilike("name", name, MatchMode.START)).addOrder(Order.asc("name")).addOrder( - Order.asc("retired")).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(EncounterType.class); + Root root = cq.from(EncounterType.class); + + // Case-insensitive 'like' predicate + Predicate namePredicate = cb.like(cb.lower(root.get("name")), MatchMode.START.toLowerCasePattern(name)); + + cq.where(namePredicate).orderBy(cb.asc(root.get("name")), cb.asc(root.get("retired"))); + + return session.createQuery(cq).getResultList(); } /** @@ -268,23 +301,31 @@ public EncounterType getEncounterTypeByUuid(String uuid) { * boolean) */ @Override - @SuppressWarnings("unchecked") public List getEncounters(String query, Integer patientId, Integer start, Integer length, boolean includeVoided) { if (StringUtils.isBlank(query) && patientId == null) { return Collections.emptyList(); } + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Encounter.class); + Root root = cq.from(Encounter.class); + + QueryResult queryResult = createEncounterByQueryPredicates(cb, root, query, patientId, includeVoided, true); - Criteria criteria = createEncounterByQueryCriteria(query, patientId, includeVoided, true); - + cq.where(queryResult.getPredicates().toArray(new Predicate[]{})) + .orderBy(queryResult.getOrders()); + + TypedQuery typedQuery = session.createQuery(cq); if (start != null) { - criteria.setFirstResult(start); + typedQuery.setFirstResult(start); } if (length != null && length > 0) { - criteria.setMaxResults(length); + typedQuery.setMaxResults(length); } - return criteria.list(); + return typedQuery.getResultList().stream().distinct().collect(Collectors.toList()); } /** @@ -310,20 +351,33 @@ public Location getSavedEncounterLocation(Encounter encounter) { */ @Override public Map> getAllEncounters(Cohort patients) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Encounter.class); + Root root = cq.from(Encounter.class); + + List predicates = createEncounterPredicates(cb, root, patients); + cq.where(predicates.toArray(new Predicate[]{})); + + cq.orderBy( + cb.desc(root.get("patient").get("personId")), + cb.desc(root.get("encounterDatetime")) + ); + + TypedQuery query = session.createQuery(cq); + query.setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS); + query.setHint("javax.persistence.cache.storeMode", CacheStoreMode.BYPASS); + Map> encountersBypatient = new HashMap<>(); - - @SuppressWarnings("unchecked") - List allEncounters = createEncounterCriteria(patients).list(); - - // set up the return map + List allEncounters = query.getResultList(); for (Encounter encounter : allEncounters) { Integer patientId = encounter.getPatient().getPersonId(); List encounters = encountersBypatient.get(patientId); - + if (encounters == null) { encounters = new ArrayList<>(); } - + encounters.add(encounter); if (!encountersBypatient.containsKey(patientId)) { encountersBypatient.put(patientId, encounters); @@ -331,41 +385,44 @@ public Map> getAllEncounters(Cohort patients) { } return encountersBypatient; } - + + /** * Create the criteria for fetching all encounters based on cohort * * @param patients * @return a map of patient with their encounters */ - private Criteria createEncounterCriteria(Cohort patients) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Encounter.class); - criteria.setCacheMode(org.hibernate.CacheMode.IGNORE); - + private List createEncounterPredicates(CriteriaBuilder cb, Root root, Cohort patients) { + List predicates = new ArrayList<>(); + predicates.add(cb.isFalse(root.get("voided"))); + // only include this where clause if patients were passed in if (patients != null) { - ArrayList patientIds = new ArrayList<>(); + ArrayList patientIds = new ArrayList<>(); patients.getMemberships().forEach(m -> patientIds.add(m.getPatientId())); - criteria.add(Restrictions.in("patient.personId", patientIds)); + predicates.add(root.get("patient").get("personId").in(patientIds)); } - - criteria.add(Restrictions.eq("voided", false)); - - criteria.addOrder(Order.desc("patient.personId")); - criteria.addOrder(Order.desc("encounterDatetime")); - return criteria; + + return predicates; } - + /** * @see org.openmrs.api.db.EncounterDAO#getCountOfEncounters(java.lang.String, * java.lang.Integer, boolean) */ @Override public Long getCountOfEncounters(String query, Integer patientId, boolean includeVoided) { - Criteria criteria = createEncounterByQueryCriteria(query, patientId, includeVoided, false); - - criteria.setProjection(Projections.countDistinct("enc.encounterId")); - return (Long) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(Encounter.class); + + QueryResult queryResult = createEncounterByQueryPredicates(cb, root, query, patientId, includeVoided, false); + cq.select(cb.countDistinct(root.get("encounterId"))) + .where(queryResult.getPredicates().toArray(new Predicate[]{})); + + return session.createQuery(cq).getSingleResult(); } /** @@ -376,46 +433,53 @@ public Long getCountOfEncounters(String query, Integer patientId, boolean includ * @param patientId the patient id * @param includeVoided Specifies whether voided encounters should be included * @param orderByNames specifies whether the encounters should be ordered by person names - * @return Criteria + * @return List */ - private Criteria createEncounterByQueryCriteria(String query, Integer patientId, boolean includeVoided, - boolean orderByNames) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Encounter.class, "enc"); + private QueryResult createEncounterByQueryPredicates(CriteriaBuilder cb, Root encounterRoot, String query, + Integer patientId, boolean includeVoided, boolean orderByNames) { + List predicates = new ArrayList<>(); + if (!includeVoided) { - criteria.add(Restrictions.eq("enc.voided", false)); + predicates.add(cb.isFalse(encounterRoot.get("voided"))); } - - criteria = criteria.createCriteria("patient", "pat"); + + Join patientJoin = encounterRoot.join("patient"); if (patientId != null) { - criteria.add(Restrictions.eq("pat.patientId", patientId)); + predicates.add(cb.equal(patientJoin.get("patientId"), patientId)); + if (StringUtils.isNotBlank(query)) { - criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); - //match on location.name, encounterType.name, form.name - //provider.name, provider.identifier, provider.person.names - MatchMode mode = MatchMode.ANYWHERE; - criteria.createAlias("enc.location", "loc"); - criteria.createAlias("enc.encounterType", "encType"); - criteria.createAlias("enc.form", "form"); - criteria.createAlias("enc.encounterProviders", "enc_prov"); - criteria.createAlias("enc_prov.provider", "prov"); - criteria.createAlias("prov.person", "person", JoinType.LEFT_OUTER_JOIN); - criteria.createAlias("person.names", "personName", JoinType.LEFT_OUTER_JOIN); - - Disjunction or = Restrictions.disjunction(); - or.add(Restrictions.ilike("loc.name", query, mode)); - or.add(Restrictions.ilike("encType.name", query, mode)); - or.add(Restrictions.ilike("form.name", query, mode)); - or.add(Restrictions.ilike("prov.name", query, mode)); - or.add(Restrictions.ilike("prov.identifier", query, mode)); - + Join locationJoin = encounterRoot.join("location"); + Join encounterTypeJoin = encounterRoot.join("encounterType"); + Join formJoin = encounterRoot.join("form"); + Join encounterProviderJoin = encounterRoot.join("encounterProviders"); + Join providerJoin = encounterProviderJoin.join("provider"); + Join personJoin = providerJoin.join("person", JoinType.LEFT); + Join personNameJoin = personJoin.join("names", JoinType.LEFT); + + String queryMatchingPattern = MatchMode.ANYWHERE.toLowerCasePattern(query); + Predicate locationNamePredicate = cb.like(cb.lower(locationJoin.get("name")), queryMatchingPattern); + Predicate encounterTypeNamePredicate = cb.like(cb.lower(encounterTypeJoin.get("name")), queryMatchingPattern); + Predicate formNamePredicate = cb.like(cb.lower(formJoin.get("name")), queryMatchingPattern); + Predicate providerNamePredicate = cb.like(cb.lower(providerJoin.get("name")), queryMatchingPattern); + Predicate providerIdentifierPredicate = cb.like(cb.lower(providerJoin.get("identifier")), queryMatchingPattern); + + List orPredicates = new ArrayList<>(); + orPredicates.add(locationNamePredicate); + orPredicates.add(encounterTypeNamePredicate); + orPredicates.add(formNamePredicate); + orPredicates.add(providerNamePredicate); + orPredicates.add(providerIdentifierPredicate); + String[] splitNames = query.split(" "); - Disjunction nameOr = Restrictions.disjunction(); + List personNamePredicates = new ArrayList<>(); for (String splitName : splitNames) { - nameOr.add(Restrictions.ilike("personName.givenName", splitName, mode)); - nameOr.add(Restrictions.ilike("personName.middleName", splitName, mode)); - nameOr.add(Restrictions.ilike("personName.familyName", splitName, mode)); - nameOr.add(Restrictions.ilike("personName.familyName2", splitName, mode)); + String splitNamePattern = MatchMode.ANYWHERE.toLowerCasePattern(splitName); + personNamePredicates.add(cb.like(cb.lower(personNameJoin.get("givenName")), splitNamePattern)); + personNamePredicates.add(cb.like(cb.lower(personNameJoin.get("middleName")), splitNamePattern)); + personNamePredicates.add(cb.like(cb.lower(personNameJoin.get("familyName")), splitNamePattern)); + personNamePredicates.add(cb.like(cb.lower(personNameJoin.get("familyName2")), splitNamePattern)); } + //OUTPUT for provider criteria: //prov.name like '%query%' OR prov.identifier like '%query%' //OR ( personName.voided = false @@ -425,40 +489,49 @@ private Criteria createEncounterByQueryCriteria(String query, Integer patientId, // OR personName.familyName2 like '%query%' // ) // ) - Conjunction personNameConjuction = Restrictions.conjunction(); - personNameConjuction.add(Restrictions.eq("personName.voided", false)); - personNameConjuction.add(nameOr); - - or.add(personNameConjuction); - - criteria.add(or); + + Predicate nameOr = cb.or(personNamePredicates.toArray(new Predicate[]{})); + + Predicate notVoided = cb.isFalse(personNameJoin.get("voided")); + Predicate personNameConjunction = cb.and(notVoided, nameOr); + orPredicates.add(personNameConjunction); + + predicates.add(cb.or(orPredicates.toArray(new Predicate[]{}))); } + return new QueryResult(predicates, Collections.emptyList()); } else { //As identifier could be all alpha, no heuristic here will work in determining intent of user for querying by name versus identifier //So search by both! - criteria = new PatientSearchCriteria(sessionFactory, criteria).prepareCriteria(query, query, - new ArrayList<>(), true, orderByNames, true); + QueryResult queryResult = new PatientSearchCriteria(sessionFactory).prepareCriteria(cb, patientJoin, query, query, + new ArrayList<>(), true, orderByNames, true); + queryResult.addPredicates(predicates); + return queryResult; } - - return criteria; } /** * @see org.openmrs.api.db.EncounterDAO#getEncountersByVisit(Visit, boolean) */ @Override - @SuppressWarnings("unchecked") public List getEncountersByVisit(Visit visit, boolean includeVoided) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Encounter.class).add( - Restrictions.eq("visit", visit)); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Encounter.class); + Root root = cq.from(Encounter.class); + + List predicates = new ArrayList<>(); + predicates.add(cb.equal(root.get("visit"), visit)); + if (!includeVoided) { - crit.add(Restrictions.eq("voided", false)); + predicates.add(cb.isFalse(root.get("voided"))); } - crit.addOrder(Order.asc("encounterDatetime")); - - return crit.list(); + + cq.where(predicates.toArray(new Predicate[]{})) + .orderBy(cb.asc(root.get("encounterDatetime"))); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.EncounterDAO#saveEncounterRole(EncounterRole encounterRole) */ @@ -481,7 +554,7 @@ public void deleteEncounterRole(EncounterRole encounterRole) throws DAOException */ @Override public EncounterRole getEncounterRole(Integer encounterRoleId) throws DAOException { - return (EncounterRole) sessionFactory.getCurrentSession().get(EncounterRole.class, encounterRoleId); + return sessionFactory.getCurrentSession().get(EncounterRole.class, encounterRoleId); } /** @@ -496,21 +569,34 @@ public EncounterRole getEncounterRoleByUuid(String uuid) { * @see org.openmrs.api.db.EncounterDAO#getAllEncounterRoles(boolean) */ @Override - public List getAllEncounterRoles(boolean includeRetired) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(EncounterRole.class); - return includeRetired ? criteria.list() : criteria.add(Restrictions.eq("retired", includeRetired)).list(); + public List getAllEncounterRoles(boolean includeRetired) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(EncounterRole.class); + Root root = cq.from(EncounterRole.class); + + if (!includeRetired) { + cq.where(cb.equal(root.get("retired"), includeRetired)); + } + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.EncounterDAO#getEncounterRoleByName(String) */ @Override - public EncounterRole getEncounterRoleByName(String name) throws DAOException { - return (EncounterRole) sessionFactory.getCurrentSession().createCriteria(EncounterRole.class).add( - Restrictions.eq("name", name)).uniqueResult(); - + public EncounterRole getEncounterRoleByName(String name) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(EncounterRole.class); + Root root = cq.from(EncounterRole.class); + + cq.where(cb.equal(root.get("name"), name)); + + return session.createQuery(cq).uniqueResult(); } - + /** * Convenience method since this DAO fetches several different domain objects by uuid * @@ -518,59 +604,77 @@ public EncounterRole getEncounterRoleByName(String name) throws DAOException { * @param table a simple classname (e.g. "Encounter") * @return */ - @SuppressWarnings("unchecked") private T getClassByUuid(Class clazz, String uuid) { - return (T) sessionFactory.getCurrentSession().createCriteria(clazz).add(Restrictions.eq("uuid", uuid)) - .uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, clazz, uuid); } - @SuppressWarnings("unchecked") @Override - public List getEncountersNotAssignedToAnyVisit(Patient patient) throws DAOException { - return sessionFactory.getCurrentSession().createCriteria(Encounter.class).add(Restrictions.eq("patient", patient)) - .add(Restrictions.isNull("visit")).add(Restrictions.eq("voided", false)).addOrder( - Order.desc("encounterDatetime")).setMaxResults(100).list(); + public List getEncountersNotAssignedToAnyVisit(Patient patient) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Encounter.class); + Root root = cq.from(Encounter.class); + + List predicates = new ArrayList<>(); + predicates.add(cb.equal(root.get("patient"), patient)); + predicates.add(cb.isNull(root.get("visit"))); + predicates.add(cb.isFalse(root.get("voided"))); + + cq.where(predicates.toArray(new Predicate[]{})) + .orderBy(cb.desc(root.get("encounterDatetime"))); + + return session.createQuery(cq).setMaxResults(100).getResultList(); } - + + /** * @see org.openmrs.api.db.EncounterDAO#getEncountersByVisitsAndPatient(org.openmrs.Patient, * boolean, java.lang.String, java.lang.Integer, java.lang.Integer) */ @Override public List getEncountersByVisitsAndPatient(Patient patient, boolean includeVoided, String query, - Integer start, Integer length) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Encounter.class); - addEncountersByPatientCriteria(criteria, patient, includeVoided, query); - criteria.addOrder(Order.desc("visit.startDatetime")); - criteria.addOrder(Order.desc("visit.visitId")); - criteria.addOrder(Order.desc("encounterDatetime")); - criteria.addOrder(Order.desc("encounterId")); - - @SuppressWarnings("unchecked") - List encounters = criteria.list(); - - criteria = sessionFactory.getCurrentSession().createCriteria(Visit.class); - addEmptyVisitsByPatientCriteria(criteria, patient, includeVoided, query); - criteria.addOrder(Order.desc("startDatetime")); - criteria.addOrder(Order.desc("visitId")); - - @SuppressWarnings("unchecked") - List emptyVisits = criteria.list(); - + Integer start, Integer length) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + + // Query for Encounters + CriteriaQuery encounterQuery = cb.createQuery(Encounter.class); + Root encounterRoot = encounterQuery.from(Encounter.class); + encounterQuery.where(createEncountersByPatientPredicates(cb, encounterRoot, patient, includeVoided, query) + .toArray(new Predicate[]{})); + encounterQuery.orderBy( + cb.desc(encounterRoot.get("visit").get("startDatetime")), + cb.desc(encounterRoot.get("visit").get("visitId")), + cb.desc(encounterRoot.get("encounterDatetime")), + cb.desc(encounterRoot.get("encounterId"))); + + List encounters = session.createQuery(encounterQuery).getResultList(); + + // Query for Empty Visits + CriteriaQuery visitQuery = cb.createQuery(Visit.class); + Root visitRoot = visitQuery.from(Visit.class); + visitQuery.where(createEmptyVisitsByPatientPredicates(cb, visitRoot, patient, includeVoided, query) + .toArray(new Predicate[]{})); + visitQuery.orderBy( + cb.desc(visitRoot.get("startDatetime")), + cb.desc(visitRoot.get("visitId"))); + + List emptyVisits = session.createQuery(visitQuery).getResultList(); + if (!emptyVisits.isEmpty()) { for (Visit emptyVisit : emptyVisits) { Encounter mockEncounter = new Encounter(); mockEncounter.setVisit(emptyVisit); encounters.add(mockEncounter); } - + encounters.sort((o1, o2) -> { Date o1Date = (o1.getVisit() != null) ? o1.getVisit().getStartDatetime() : o1.getEncounterDatetime(); Date o2Date = (o2.getVisit() != null) ? o2.getVisit().getStartDatetime() : o2.getEncounterDatetime(); return o2Date.compareTo(o1Date); }); } - + if (start == null) { start = 0; } @@ -581,82 +685,110 @@ public List getEncountersByVisitsAndPatient(Patient patient, boolean if (end > encounters.size()) { end = encounters.size(); } - + return encounters.subList(start, end); } - + /** * @see org.openmrs.api.db.EncounterDAO#getEncountersByVisitsAndPatientCount(org.openmrs.Patient, * boolean, java.lang.String) */ @Override public Integer getEncountersByVisitsAndPatientCount(Patient patient, boolean includeVoided, String query) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Visit.class); - addEmptyVisitsByPatientCriteria(criteria, patient, includeVoided, query); - - criteria.setProjection(Projections.rowCount()); - Integer count = ((Number) criteria.uniqueResult()).intValue(); - - criteria = sessionFactory.getCurrentSession().createCriteria(Encounter.class); - addEncountersByPatientCriteria(criteria, patient, includeVoided, query); - - criteria.setProjection(Projections.rowCount()); - count = count + ((Number) criteria.uniqueResult()).intValue(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery visitQuery = cb.createQuery(Long.class); + Root visitRoot = visitQuery.from(Visit.class); - return count; + visitQuery.select(cb.count(visitRoot)); + + List visitPredicates = createEmptyVisitsByPatientPredicates(cb, visitRoot, patient, includeVoided, query); + visitQuery.where(visitPredicates.toArray(new Predicate[]{})); + + Long visitCount = session.createQuery(visitQuery).getSingleResult(); + + CriteriaQuery encounterQuery = cb.createQuery(Long.class); + Root encounterRoot = encounterQuery.from(Encounter.class); + encounterQuery.select(cb.count(encounterRoot)); + + List encounterPredicates = createEncountersByPatientPredicates(cb, encounterRoot, patient, includeVoided, query); + encounterQuery.where(encounterPredicates.toArray(new Predicate[]{})); + + Long encounterCount = session.createQuery(encounterQuery).getSingleResult(); + + return visitCount.intValue() + encounterCount.intValue(); } - - private void addEmptyVisitsByPatientCriteria(Criteria criteria, Patient patient, boolean includeVoided, String query) { - criteria.add(Restrictions.eq("patient", patient)); - criteria.add(Restrictions.isEmpty("encounters")); - + + private List createEmptyVisitsByPatientPredicates(CriteriaBuilder cb, Root root, + Patient patient, boolean includeVoided, String query) { + List predicates = new ArrayList<>(); + + predicates.add(cb.equal(root.get("patient"), patient)); + predicates.add(cb.isEmpty(root.get("encounters"))); + if (!includeVoided) { - criteria.add(Restrictions.eq("voided", includeVoided)); + predicates.add(cb.isFalse(root.get("voided"))); } - + if (query != null && !StringUtils.isBlank(query)) { - criteria.createAlias("visitType", "visitType", JoinType.LEFT_OUTER_JOIN); - criteria.createAlias("location", "location", JoinType.LEFT_OUTER_JOIN); - - Disjunction or = Restrictions.disjunction(); - criteria.add(or); - or.add(Restrictions.ilike("visitType.name", query, MatchMode.ANYWHERE)); - or.add(Restrictions.ilike("location.name", query, MatchMode.ANYWHERE)); + Join visitTypeJoin = root.join("visitType", JoinType.LEFT); + Join locationJoin = root.join("location", JoinType.LEFT); + + predicates.add( + cb.or( + cb.like(cb.lower(visitTypeJoin.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(query)), + cb.like(cb.lower(locationJoin.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(query)) + ) + ); } - + + return predicates; } - - private void addEncountersByPatientCriteria(Criteria criteria, Patient patient, boolean includeVoided, String query) { - criteria.add(Restrictions.eq("patient", patient)); - criteria.createAlias("visit", "visit", JoinType.LEFT_OUTER_JOIN); - + + private List createEncountersByPatientPredicates(CriteriaBuilder cb, Root root, + Patient patient, boolean includeVoided, String query) { + List predicates = new ArrayList<>(); + + predicates.add(cb.equal(root.get("patient"), patient)); + + Join visitJoin = root.join("visit", JoinType.LEFT); + if (!includeVoided) { - criteria.add(Restrictions.eq("voided", includeVoided)); + predicates.add(cb.equal(root.get("voided"), false)); } - + if (query != null && !StringUtils.isBlank(query)) { - criteria.createAlias("visit.visitType", "visitType", JoinType.LEFT_OUTER_JOIN); - criteria.createAlias("visit.location", "visitLocation", JoinType.LEFT_OUTER_JOIN); - criteria.createAlias("location", "location", JoinType.LEFT_OUTER_JOIN); - criteria.createAlias("encounterType", "encounterType", JoinType.LEFT_OUTER_JOIN); + Join visitTypeJoin = visitJoin.join("visitType", JoinType.LEFT); + Join visitLocationJoin = visitJoin.join("location", JoinType.LEFT); + Join locationJoin = root.join("location", JoinType.LEFT); + Join encounterTypeJoin = root.join("encounterType", JoinType.LEFT); - Disjunction or = Restrictions.disjunction(); - criteria.add(or); - or.add(Restrictions.ilike("visitType.name", query, MatchMode.ANYWHERE)); - or.add(Restrictions.ilike("visitLocation.name", query, MatchMode.ANYWHERE)); - or.add(Restrictions.ilike("location.name", query, MatchMode.ANYWHERE)); - or.add(Restrictions.ilike("encounterType.name", query, MatchMode.ANYWHERE)); + String likePattern = MatchMode.ANYWHERE.toLowerCasePattern(query); + predicates.add( + cb.or( + cb.like(cb.lower(visitTypeJoin.get("name")), likePattern), + cb.like(cb.lower(visitLocationJoin.get("name")), likePattern), + cb.like(cb.lower(locationJoin.get("name")), likePattern), + cb.like(cb.lower(encounterTypeJoin.get("name")), likePattern) + ) + ); } - + + return predicates; } - + /** * @see org.openmrs.api.db.EncounterDAO#getEncounterRolesByName(String) */ - @Override - public List getEncounterRolesByName(String name) throws DAOException { - return sessionFactory.getCurrentSession().createCriteria(EncounterRole.class).add(Restrictions.eq("name", name)) - .list(); + public List getEncounterRolesByName(String name) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(EncounterRole.class); + Root root = cq.from(EncounterRole.class); + + cq.where(cb.equal(root.get("name"), name)); + + return session.createQuery(cq).getResultList(); } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateFormDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateFormDAO.java index b269f251269c..72742f6f6158 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateFormDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateFormDAO.java @@ -9,22 +9,23 @@ */ package org.openmrs.api.db.hibernate; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import javax.persistence.Query; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Subquery; + import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; -import org.hibernate.Query; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.DetachedCriteria; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Property; -import org.hibernate.criterion.Restrictions; -import org.hibernate.criterion.Subqueries; import org.openmrs.Concept; import org.openmrs.EncounterType; import org.openmrs.Field; @@ -97,39 +98,52 @@ public void deleteForm(Form form) throws DAOException { */ @Override public Form getForm(Integer formId) throws DAOException { - return (Form) sessionFactory.getCurrentSession().get(Form.class, formId); + return sessionFactory.getCurrentSession().get(Form.class, formId); } /** * @see org.openmrs.api.FormService#getFormFields(Form) */ - @SuppressWarnings("unchecked") - public List getFormFields(Form form) throws DAOException { - return sessionFactory.getCurrentSession().createCriteria(FormField.class, "ff") - .add(Restrictions.eq("ff.form", form)).list(); + public List getFormFields(Form form) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(FormField.class); + Root root = cq.from(FormField.class); + + cq.where(cb.equal(root.get("form"), form)); + + return session.createQuery(cq).getResultList(); } /** * @see org.openmrs.api.db.FormDAO#getFields(java.lang.String) */ @Override - @SuppressWarnings("unchecked") - public List getFields(String search) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Field.class); - criteria.add(Restrictions.like("name", search, MatchMode.ANYWHERE)); - criteria.addOrder(Order.asc("name")); - return criteria.list(); + public List getFields(String search) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Field.class); + Root root = cq.from(Field.class); + + cq.where(cb.like(root.get("name"), MatchMode.ANYWHERE.toCaseSensitivePattern(search))); + cq.orderBy(cb.asc(root.get("name"))); + + return session.createQuery(cq).getResultList(); } /** * @see org.openmrs.api.FormService#getFieldsByConcept(org.openmrs.Concept) */ - @SuppressWarnings("unchecked") - public List getFieldsByConcept(Concept concept) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Field.class); - criteria.add(Restrictions.eq("concept", concept)); - criteria.addOrder(Order.asc("name")); - return criteria.list(); + public List getFieldsByConcept(Concept concept) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Field.class); + Root root = cq.from(Field.class); + + cq.where(cb.equal(root.get("concept"), concept)) + .orderBy(cb.asc(root.get("name"))); + + return session.createQuery(cq).getResultList(); } /** @@ -138,7 +152,7 @@ public List getFieldsByConcept(Concept concept) throws DAOException { */ @Override public Field getField(Integer fieldId) throws DAOException { - return (Field) sessionFactory.getCurrentSession().get(Field.class, fieldId); + return sessionFactory.getCurrentSession().get(Field.class, fieldId); } /** @@ -146,15 +160,17 @@ public Field getField(Integer fieldId) throws DAOException { * @see org.openmrs.api.db.FormDAO#getAllFields(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllFields(boolean includeRetired) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Field.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Field.class); + Root root = criteriaQuery.from(Field.class); + if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + criteriaQuery.where(criteriaBuilder.isFalse(root.get("retired"))); } - - return crit.list(); + + return session.createQuery(criteriaQuery).getResultList(); } /** @@ -163,7 +179,7 @@ public List getAllFields(boolean includeRetired) throws DAOException { */ @Override public FieldType getFieldType(Integer fieldTypeId) throws DAOException { - return (FieldType) sessionFactory.getCurrentSession().get(FieldType.class, fieldTypeId); + return sessionFactory.getCurrentSession().get(FieldType.class, fieldTypeId); } /** @@ -171,15 +187,17 @@ public FieldType getFieldType(Integer fieldTypeId) throws DAOException { * @see org.openmrs.api.db.FormDAO#getAllFieldTypes(boolean) */ @Override - @SuppressWarnings("unchecked") - public List getAllFieldTypes(boolean includeRetired) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(FieldType.class); - + public List getAllFieldTypes(boolean includeRetired) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(FieldType.class); + Root root = cq.from(FieldType.class); + if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - return crit.list(); + + return session.createQuery(cq).getResultList(); } /** @@ -188,7 +206,7 @@ public List getAllFieldTypes(boolean includeRetired) throws DAOExcept */ @Override public FormField getFormField(Integer formFieldId) throws DAOException { - return (FormField) sessionFactory.getCurrentSession().get(FormField.class, formFieldId); + return sessionFactory.getCurrentSession().get(FormField.class, formFieldId); } /** @@ -205,11 +223,16 @@ public FormField getFormField(Form form, Concept concept, Collection log.debug("form is null, no fields will be matched"); return null; } - Criteria crit = sessionFactory.getCurrentSession().createCriteria(FormField.class, "ff").createAlias("field", - "field").add(Restrictions.eq("field.concept", concept)).add(Restrictions.eq("form", form)); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(FormField.class); + Root root = cq.from(FormField.class); - // get the list of all formfields with this concept for this form - List formFields = crit.list(); + Join fieldJoin = root.join("field"); + + cq.where(cb.equal(fieldJoin.get("concept"), concept), cb.equal(root.get("form"), form)); + + List formFields = session.createQuery(cq).getResultList(); String err = "FormField warning. No FormField matching concept '" + concept + "' for form '" + form + "'"; @@ -243,18 +266,19 @@ public FormField getFormField(Form form, Concept concept, Collection * @see org.openmrs.api.FormService#getForms() */ @Override - @SuppressWarnings("unchecked") - public List
getAllForms(boolean includeRetired) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Form.class); - + public List getAllForms(boolean includeRetired) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Form.class); + Root root = cq.from(Form.class); + if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - crit.addOrder(Order.asc("name")); - crit.addOrder(Order.asc("formId")); - - return crit.list(); + + cq.orderBy(cb.asc(root.get("name")), cb.asc(root.get("formId"))); + + return session.createQuery(cq).getResultList(); } /** @@ -265,9 +289,9 @@ public List getAllForms(boolean includeRetired) throws DAOException { public List getFormsContainingConcept(Concept c) throws DAOException { String q = "select distinct ff.form from FormField ff where ff.field.concept = :concept"; Query query = sessionFactory.getCurrentSession().createQuery(q); - query.setEntity("concept", c); + query.setParameter("concept", c); - return query.list(); + return query.getResultList(); } /** @@ -311,10 +335,13 @@ public void deleteFormField(FormField formField) throws DAOException { * @see org.openmrs.api.db.FormDAO#getAllFormFields() */ @Override - @SuppressWarnings("unchecked") - public List getAllFormFields() throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(FormField.class); - return crit.list(); + public List getAllFormFields() { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(FormField.class); + cq.from(FormField.class); + + return session.createQuery(cq).getResultList(); } /** @@ -323,64 +350,71 @@ public List getAllFormFields() throws DAOException { * java.util.Collection, java.util.Collection, java.lang.Boolean) */ @Override - @SuppressWarnings("unchecked") public List getFields(Collection forms, Collection fieldTypes, Collection concepts, Collection tableNames, Collection attributeNames, Boolean selectMultiple, Collection containsAllAnswers, Collection containsAnyAnswer, Boolean retired) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Field.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Field.class); + Root root = cq.from(Field.class); + List predicates = new ArrayList<>(); if (!forms.isEmpty()) { - crit.add(Restrictions.in("form", forms)); + predicates.add(root.get("form").in(forms)); } - + if (!fieldTypes.isEmpty()) { - crit.add(Restrictions.in("fieldType", fieldTypes)); + predicates.add(root.get("fieldType").in(fieldTypes)); } - + if (!concepts.isEmpty()) { - crit.add(Restrictions.in("concept", concepts)); + predicates.add(root.get("concept").in(concepts)); } - + if (!tableNames.isEmpty()) { - crit.add(Restrictions.in("tableName", tableNames)); + predicates.add(root.get("tableName").in(tableNames)); } - + if (!attributeNames.isEmpty()) { - crit.add(Restrictions.in("attributeName", attributeNames)); + predicates.add(root.get("attributeName").in(attributeNames)); } - + if (selectMultiple != null) { - crit.add(Restrictions.eq("selectMultiple", selectMultiple)); + predicates.add(cb.equal(root.get("selectMultiple"), selectMultiple)); } - + if (!containsAllAnswers.isEmpty()) { throw new APIException("Form.getFields.error", new Object[] { "containsAllAnswers" }); } - + if (!containsAnyAnswer.isEmpty()) { throw new APIException("Form.getFields.error", new Object[] { "containsAnyAnswer" }); } - + if (retired != null) { - crit.add(Restrictions.eq("retired", retired)); + predicates.add(cb.equal(root.get("retired"), retired)); } - - return crit.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } /** * @see org.openmrs.api.db.FormDAO#getForm(java.lang.String, java.lang.String) */ @Override - public Form getForm(String name, String version) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Form.class); - - crit.add(Restrictions.eq("name", name)); - crit.add(Restrictions.eq("version", version)); - - return (Form) crit.uniqueResult(); + public Form getForm(String name, String version) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Form.class); + Root root = cq.from(Form.class); + + cq.where(cb.equal(root.get("name"), name), cb.equal(root.get("version"), version)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -389,15 +423,18 @@ public Form getForm(String name, String version) throws DAOException { * java.util.Collection) */ @Override - @SuppressWarnings("unchecked") public List getForms(String partialName, Boolean published, Collection encounterTypes, Boolean retired, Collection containingAnyFormField, Collection containingAllFormFields, Collection fields) throws DAOException { - - Criteria crit = getFormCriteria(partialName, published, encounterTypes, retired, containingAnyFormField, - containingAllFormFields, fields); - - return crit.list(); + CriteriaBuilder cb = sessionFactory.getCurrentSession().getCriteriaBuilder(); + + CriteriaQuery cq = cb.createQuery(Form.class); + Root root = cq.from(Form.class); + List predicates = getFormCriteria(cb, cq, root, partialName, published, encounterTypes, retired, containingAnyFormField, + containingAllFormFields, fields); + cq.where(predicates.toArray(new Predicate[]{})); + + return sessionFactory.getCurrentSession().createQuery(cq).getResultList(); } /** @@ -409,19 +446,28 @@ public List getForms(String partialName, Boolean published, Collection encounterTypes, Boolean retired, Collection containingAnyFormField, Collection containingAllFormFields, Collection fields) throws DAOException { - - Criteria crit = getFormCriteria(partialName, published, encounterTypes, retired, containingAnyFormField, + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(Form.class); + + List predicates = getFormCriteria(cb, cq, root, partialName, published, encounterTypes, retired, containingAnyFormField, containingAllFormFields, fields); - - crit.setProjection(Projections.count("formId")); - - return OpenmrsUtil.convertToInteger((Long) crit.uniqueResult()); + + cq.select(cb.count(root.get("formId"))) + .where(predicates.toArray(new Predicate[]{})); + + Long result = sessionFactory.getCurrentSession().createQuery(cq).getSingleResult(); + return OpenmrsUtil.convertToInteger(result); } /** - * Convenience method to create the same hibernate criteria object for both getForms and + * Convenience method to create the same predicates for both getForms and * getFormCount * + * @param cb + * @param cq + * @param root * @param partialName * @param published * @param encounterTypes @@ -431,27 +477,30 @@ public Integer getFormCount(String partialName, Boolean published, Collection encounterTypes, + private List getFormCriteria(CriteriaBuilder cb, CriteriaQuery cq, Root root, String partialName, Boolean published, Collection encounterTypes, Boolean retired, Collection containingAnyFormField, Collection containingAllFormFields, Collection fields) { - - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Form.class, "f"); + List predicates = new ArrayList<>(); + if (StringUtils.isNotEmpty(partialName)) { - crit.add(Restrictions.or(Restrictions.like("name", partialName, MatchMode.START), Restrictions.like("name", " " - + partialName, MatchMode.ANYWHERE))); + predicates.add(cb.or( + cb.like(root.get("name"), MatchMode.START.toCaseSensitivePattern(partialName)), + cb.like(root.get("name"), MatchMode.ANYWHERE.toCaseSensitivePattern(" " + partialName))) + ); } + if (published != null) { - crit.add(Restrictions.eq("published", published)); + predicates.add(cb.equal(root.get("published"), published)); } - + if (!encounterTypes.isEmpty()) { - crit.add(Restrictions.in("encounterType", encounterTypes)); + predicates.add(root.get("encounterType").in(encounterTypes)); } - + if (retired != null) { - crit.add(Restrictions.eq("retired", retired)); + predicates.add(cb.equal(root.get("retired"), retired)); } - + // TODO junit test if (!containingAnyFormField.isEmpty()) { // Convert form field persistents to integers @@ -459,13 +508,15 @@ private Criteria getFormCriteria(String partialName, Boolean published, Collecti for (FormField ff : containingAnyFormField) { anyFormFieldIds.add(ff.getFormFieldId()); } - - DetachedCriteria subquery = DetachedCriteria.forClass(FormField.class, "ff"); - subquery.setProjection(Projections.property("ff.form.formId")); - subquery.add(Restrictions.in("ff.formFieldId", anyFormFieldIds)); - crit.add(Subqueries.propertyIn("f.formId", subquery)); + + Subquery subquery = cq.subquery(Integer.class); + Root subqueryRoot = subquery.from(FormField.class); + subquery.select(subqueryRoot.get("form").get("formId")); + subquery.where(subqueryRoot.get("formFieldId").in(anyFormFieldIds)); + + predicates.add(root.get("formId").in(subquery)); } - + if (!containingAllFormFields.isEmpty()) { // Convert form field persistents to integers @@ -473,22 +524,28 @@ private Criteria getFormCriteria(String partialName, Boolean published, Collecti for (FormField ff : containingAllFormFields) { allFormFieldIds.add(ff.getFormFieldId()); } - DetachedCriteria subquery = DetachedCriteria.forClass(FormField.class, "ff"); - subquery.setProjection(Projections.count("ff.formFieldId")). - add(Property.forName("ff.form.formId").eqProperty("f.formId")). - add(Restrictions.in("ff.formFieldId", allFormFieldIds)); - crit.add(Subqueries.eq((long) containingAllFormFields.size(), subquery)); - + Subquery subquery = cq.subquery(Long.class); + Root subqueryRoot = subquery.from(FormField.class); + + subquery.select(cb.count(subqueryRoot.get("formFieldId"))); + subquery.where( + cb.equal(subqueryRoot.get("form").get("formId"), root.get("formId")), + subqueryRoot.get("formFieldId").in(allFormFieldIds) + ); + predicates.add(cb.equal(cb.literal((long) containingAllFormFields.size()), subquery.getSelection())); } - + // get all forms (dupes included) that have this field on them if (!fields.isEmpty()) { - Criteria crit2 = crit.createCriteria("formFields", "ff"); - crit2.add(Restrictions.eqProperty("ff.form.formId", "form.formId")); - crit2.add(Restrictions.in("ff.field", fields)); + // Create join object here + Join joinFormFields = root.join("formFields"); + Join joinForm = joinFormFields.join("form"); + + predicates.add(cb.equal(joinFormFields.get("form").get("formId"), joinForm.get("formId"))); + predicates.add(joinFormFields.get("field").in(fields)); } - - return crit; + + return predicates; } /** @@ -496,14 +553,12 @@ private Criteria getFormCriteria(String partialName, Boolean published, Collecti */ @Override public Field getFieldByUuid(String uuid) { - return (Field) sessionFactory.getCurrentSession().createQuery("from Field f where f.uuid = :uuid").setString("uuid", - uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Field.class, uuid); } @Override public FieldAnswer getFieldAnswerByUuid(String uuid) { - return (FieldAnswer) sessionFactory.getCurrentSession().createQuery("from FieldAnswer f where f.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, FieldAnswer.class, uuid); } /** @@ -511,8 +566,7 @@ public FieldAnswer getFieldAnswerByUuid(String uuid) { */ @Override public FieldType getFieldTypeByUuid(String uuid) { - return (FieldType) sessionFactory.getCurrentSession().createQuery("from FieldType ft where ft.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, FieldType.class, uuid); } /** @@ -520,8 +574,13 @@ public FieldType getFieldTypeByUuid(String uuid) { */ @Override public FieldType getFieldTypeByName(String name) { - return (FieldType) sessionFactory.getCurrentSession().createQuery("from FieldType ft where ft.name = :name") - .setString("name", name).uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(FieldType.class); + Root root = cq.from(FieldType.class); + + cq.where(cb.equal(root.get("name"), name)); + return session.createQuery(cq).uniqueResult(); } /** @@ -529,8 +588,7 @@ public FieldType getFieldTypeByName(String name) { */ @Override public Form getFormByUuid(String uuid) { - return (Form) sessionFactory.getCurrentSession().createQuery("from Form f where f.uuid = :uuid").setString("uuid", - uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Form.class, uuid); } /** @@ -538,23 +596,23 @@ public Form getFormByUuid(String uuid) { */ @Override public FormField getFormFieldByUuid(String uuid) { - return (FormField) sessionFactory.getCurrentSession().createQuery("from FormField ff where ff.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, FormField.class, uuid); } /** * @see org.openmrs.api.db.FormDAO#getFormsByName(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List getFormsByName(String name) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Form.class); - - crit.add(Restrictions.eq("name", name)); - crit.add(Restrictions.eq("retired", false)); - crit.addOrder(Order.desc("version")); - - return crit.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Form.class); + Root root = cq.from(Form.class); + + cq.where(cb.equal(root.get("name"), name), cb.isFalse(root.get("retired"))); + cq.orderBy(cb.desc(root.get("version"))); + + return session.createQuery(cq).getResultList(); } /** @@ -578,10 +636,15 @@ public FieldType saveFieldType(FieldType fieldType) throws DAOException { * @see org.openmrs.api.db.FormDAO#getFormFieldsByField(Field) */ @Override - @SuppressWarnings("unchecked") public List getFormFieldsByField(Field field) { - return sessionFactory.getCurrentSession().createQuery("from FormField f where f.field = :field").setEntity("field", - field).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(FormField.class); + Root root = cq.from(FormField.class); + + cq.where(cb.equal(root.get("field"), field)); + + return session.createQuery(cq).getResultList(); } /** @@ -589,7 +652,7 @@ public List getFormFieldsByField(Field field) { */ @Override public FormResource getFormResource(Integer formResourceId) { - return (FormResource) sessionFactory.getCurrentSession().get(FormResource.class, formResourceId); + return sessionFactory.getCurrentSession().get(FormResource.class, formResourceId); } /** @@ -597,9 +660,7 @@ public FormResource getFormResource(Integer formResourceId) { */ @Override public FormResource getFormResourceByUuid(String uuid) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(FormResource.class).add( - Restrictions.eq("uuid", uuid)); - return (FormResource) crit.uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, FormResource.class, uuid); } /** @@ -607,10 +668,14 @@ public FormResource getFormResourceByUuid(String uuid) { */ @Override public FormResource getFormResource(Form form, String name) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(FormResource.class).add( - Restrictions.and(Restrictions.eq("form", form), Restrictions.eq("name", name))); - - return (FormResource) crit.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(FormResource.class); + Root root = cq.from(FormResource.class); + + cq.where(cb.equal(root.get("form"), form), cb.equal(root.get("name"), name)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -635,9 +700,13 @@ public void deleteFormResource(FormResource formResource) { */ @Override public Collection getFormResourcesForForm(Form form) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(FormResource.class).add( - Restrictions.eq("form", form)); - return crit.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(FormResource.class); + Root root = cq.from(FormResource.class); + + cq.where(cb.equal(root.get("form"), form)); + + return session.createQuery(cq).getResultList(); } - } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateLocationDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateLocationDAO.java index 5a3c7d1d725d..c724741b4f4e 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateLocationDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateLocationDAO.java @@ -9,20 +9,22 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Subquery; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.DetachedCriteria; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Restrictions; -import org.hibernate.criterion.Subqueries; import org.openmrs.Location; import org.openmrs.LocationAttribute; import org.openmrs.LocationAttributeType; @@ -70,40 +72,49 @@ public Location saveLocation(Location location) { */ @Override public Location getLocation(Integer locationId) { - return (Location) sessionFactory.getCurrentSession().get(Location.class, locationId); + return sessionFactory.getCurrentSession().get(Location.class, locationId); } /** * @see org.openmrs.api.db.LocationDAO#getLocation(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public Location getLocation(String name) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Location.class).add( - Restrictions.eq("name", name)); - - List locations = criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Location.class); + Root locationRoot = cq.from(Location.class); + + cq.where(cb.equal(locationRoot.get("name"), name)); + + List locations = session.createQuery(cq).getResultList(); if (null == locations || locations.isEmpty()) { return null; } return locations.get(0); } - + /** * @see org.openmrs.api.db.LocationDAO#getAllLocations(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllLocations(boolean includeRetired) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Location.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Location.class); + Root locationRoot = cq.from(Location.class); + + List orderList = new ArrayList<>(); if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(locationRoot.get("retired"))); } else { - //push retired locations to the end of the returned list - criteria.addOrder(Order.asc("retired")); + orderList.add(cb.asc(locationRoot.get("retired"))); } - criteria.addOrder(Order.asc("name")); - return criteria.list(); + orderList.add(cb.asc(locationRoot.get("name"))); + + cq.orderBy(orderList); + + return session.createQuery(cq).getResultList(); } /** @@ -128,48 +139,61 @@ public LocationTag saveLocationTag(LocationTag tag) { */ @Override public LocationTag getLocationTag(Integer locationTagId) { - return (LocationTag) sessionFactory.getCurrentSession().get(LocationTag.class, locationTagId); + return sessionFactory.getCurrentSession().get(LocationTag.class, locationTagId); } - + /** * @see org.openmrs.api.db.LocationDAO#getLocationTagByName(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public LocationTag getLocationTagByName(String tag) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(LocationTag.class).add( - Restrictions.eq("name", tag)); - - List tags = criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(LocationTag.class); + Root root = cq.from(LocationTag.class); + + cq.where(cb.equal(root.get("name"), tag)); + + List tags = session.createQuery(cq).getResultList(); if (null == tags || tags.isEmpty()) { return null; } return tags.get(0); } - + /** * @see org.openmrs.api.db.LocationDAO#getAllLocationTags(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllLocationTags(boolean includeRetired) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(LocationTag.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(LocationTag.class); + Root root = cq.from(LocationTag.class); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - criteria.addOrder(Order.asc("name")); - return criteria.list(); + cq.orderBy(cb.asc(root.get("name"))); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.LocationDAO#getLocationTags(String) */ @Override - @SuppressWarnings("unchecked") public List getLocationTags(String search) { - return sessionFactory.getCurrentSession().createCriteria(LocationTag.class) + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(LocationTag.class); + Root root = cq.from(LocationTag.class); + // 'ilike' case insensitive search - .add(Restrictions.ilike("name", search, MatchMode.START)).addOrder(Order.asc("name")).list(); + cq.where(cb.like(cb.lower(root.get("name")), MatchMode.START.toLowerCasePattern(search))); + cq.orderBy(cb.asc(root.get("name"))); + + return session.createQuery(cq).getResultList(); } /** @@ -185,8 +209,7 @@ public void deleteLocationTag(LocationTag tag) { */ @Override public Location getLocationByUuid(String uuid) { - return (Location) sessionFactory.getCurrentSession().createQuery("from Location l where l.uuid = :uuid").setString( - "uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Location.class, uuid); } /** @@ -194,29 +217,36 @@ public Location getLocationByUuid(String uuid) { */ @Override public LocationTag getLocationTagByUuid(String uuid) { - return (LocationTag) sessionFactory.getCurrentSession().createQuery("from LocationTag where uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, LocationTag.class, uuid); } - + /** * @see org.openmrs.api.db.LocationDAO#getCountOfLocations(String, Boolean) */ @Override public Long getCountOfLocations(String nameFragment, Boolean includeRetired) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Location.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(Location.class); + + cq.select(cb.count(root)); + + List predicates = new ArrayList<>(); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } - + if (StringUtils.isNotBlank(nameFragment)) { - criteria.add(Restrictions.ilike("name", nameFragment, MatchMode.START)); + predicates.add(cb.like(cb.lower(root.get("name")), MatchMode.START.toLowerCasePattern(nameFragment))); } - - criteria.setProjection(Projections.rowCount()); - - return (Long) criteria.uniqueResult(); + + cq.where(cb.and(predicates.toArray(new Predicate[]{}))); + + return session.createQuery(cq).getSingleResult(); } - + /** * @see LocationDAO#getLocations(String, org.openmrs.Location, java.util.Map, boolean, Integer, Integer) */ @@ -224,61 +254,80 @@ public Long getCountOfLocations(String nameFragment, Boolean includeRetired) { public List getLocations(String nameFragment, Location parent, Map serializedAttributeValues, boolean includeRetired, Integer start, Integer length) { - - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Location.class); - + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Location.class); + Root locationRoot = cq.from(Location.class); + + List predicates = new ArrayList<>(); + if (StringUtils.isNotBlank(nameFragment)) { - criteria.add(Restrictions.ilike("name", nameFragment, MatchMode.START)); + predicates.add(cb.like(cb.lower(locationRoot.get("name")), MatchMode.START.toLowerCasePattern(nameFragment))); } - + if (parent != null) { - criteria.add(Restrictions.eq("parentLocation", parent)); + predicates.add(cb.equal(locationRoot.get("parentLocation"), parent)); } - + if (serializedAttributeValues != null) { - HibernateUtil.addAttributeCriteria(criteria, serializedAttributeValues); + predicates.addAll(HibernateUtil.getAttributePredicate(cb, locationRoot, serializedAttributeValues)); } - + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(locationRoot.get("retired"))); } - - criteria.addOrder(Order.asc("name")); + + cq.where(cb.and(predicates.toArray(new Predicate[]{}))); + cq.orderBy(cb.asc(locationRoot.get("name"))); + + TypedQuery query = session.createQuery(cq); + if (start != null) { - criteria.setFirstResult(start); + query.setFirstResult(start); } if (length != null && length > 0) { - criteria.setMaxResults(length); + query.setMaxResults(length); } - - return criteria.list(); + + return query.getResultList(); } - + /** * @see LocationDAO#getRootLocations(boolean) */ - @SuppressWarnings("unchecked") @Override public List getRootLocations(boolean includeRetired) throws DAOException { - - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Location.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Location.class); + Root locationRoot = cq.from(Location.class); + + List predicates = new ArrayList<>(); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(locationRoot.get("retired"))); } - - criteria.add(Restrictions.isNull("parentLocation")); - - criteria.addOrder(Order.asc("name")); - return criteria.list(); + + predicates.add(cb.isNull(locationRoot.get("parentLocation"))); + + cq.where(predicates.toArray(new Predicate[]{})); + cq.orderBy(cb.asc(locationRoot.get("name"))); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.LocationDAO#getAllLocationAttributeTypes() */ - @SuppressWarnings("unchecked") @Override public List getAllLocationAttributeTypes() { - return sessionFactory.getCurrentSession().createCriteria(LocationAttributeType.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(LocationAttributeType.class); + cq.from(LocationAttributeType.class); + + return session.createQuery(cq).getResultList(); } /** @@ -286,7 +335,7 @@ public List getAllLocationAttributeTypes() { */ @Override public LocationAttributeType getLocationAttributeType(Integer id) { - return (LocationAttributeType) sessionFactory.getCurrentSession().get(LocationAttributeType.class, id); + return sessionFactory.getCurrentSession().get(LocationAttributeType.class, id); } /** @@ -294,8 +343,7 @@ public LocationAttributeType getLocationAttributeType(Integer id) { */ @Override public LocationAttributeType getLocationAttributeTypeByUuid(String uuid) { - return (LocationAttributeType) sessionFactory.getCurrentSession().createCriteria(LocationAttributeType.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, LocationAttributeType.class, uuid); } /** @@ -320,39 +368,66 @@ public void deleteLocationAttributeType(LocationAttributeType locationAttributeT */ @Override public LocationAttribute getLocationAttributeByUuid(String uuid) { - return (LocationAttribute) sessionFactory.getCurrentSession().createCriteria(LocationAttribute.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, LocationAttribute.class, uuid); } - + /** * @see org.openmrs.api.db.LocationDAO#getLocationAttributeTypeByName(java.lang.String) */ @Override public LocationAttributeType getLocationAttributeTypeByName(String name) { - return (LocationAttributeType) sessionFactory.getCurrentSession().createCriteria(LocationAttributeType.class).add( - Restrictions.eq("name", name)).uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(LocationAttributeType.class); + Root root = cq.from(LocationAttributeType.class); + + cq.where(cb.equal(root.get("name"), name)); + + return session.createQuery(cq).uniqueResult(); } - + /** * @see org.openmrs.api.db.LocationDAO#getLocationsHavingAllTags(java.util.List) */ @Override public List getLocationsHavingAllTags(List tags) { tags.removeAll(Collections.singleton(null)); - - DetachedCriteria numberOfMatchingTags = DetachedCriteria.forClass(Location.class, "alias").createAlias("alias.tags", - "locationTag").add(Restrictions.in("locationTag.locationTagId", getLocationTagIds(tags))).setProjection( - Projections.rowCount()).add(Restrictions.eqProperty("alias.locationId", "outer.locationId")); - - return sessionFactory.getCurrentSession().createCriteria(Location.class, "outer").add( - Restrictions.eq("retired", false)).add(Subqueries.eq(Long.valueOf(tags.size()), numberOfMatchingTags)).list(); + + List tagIds = getLocationTagIds(tags); + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + + CriteriaQuery mainQuery = cb.createQuery(Location.class); + Root locationRoot = mainQuery.from(Location.class); + + // Create a subquery to count matching tags + Subquery tagCountSubquery = mainQuery.subquery(Long.class); + Root subRoot = tagCountSubquery.from(Location.class); + Join tagsJoin = subRoot.join("tags"); + + tagCountSubquery.select(cb.count(subRoot)) + .where(cb.and( + tagsJoin.get("locationTagId").in(tagIds), + cb.equal(subRoot.get("locationId"), locationRoot.get("locationId")) + )); + + mainQuery.select(locationRoot) + .where(cb.and( + cb.isFalse(locationRoot.get("retired")), + cb.equal(cb.literal((long) tags.size()), tagCountSubquery) + )); + + return session.createQuery(mainQuery).getResultList(); } /** * Extract locationTagIds from the list of LocationTag objects provided. * - * @param tags - * @return + * @param tags A list of LocationTag objects from which to extract the location tag IDs. + * This list should not be null. + * @return A List of Integer representing the IDs of the provided LocationTag objects. + * Returns an empty list if the input list is empty. */ private List getLocationTagIds(List tags) { List locationTagIds = new ArrayList<>(); diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateMedicationDispenseDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateMedicationDispenseDAO.java index 765abeee7ca5..c9bf7550d3ae 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateMedicationDispenseDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateMedicationDispenseDAO.java @@ -41,10 +41,7 @@ public MedicationDispense getMedicationDispense(Integer medicationDispenseId) { @Override public MedicationDispense getMedicationDispenseByUuid(String uuid) { - return sessionFactory.getCurrentSession() - .createQuery("select md from MedicationDispense md where md.uuid = :uuid", MedicationDispense.class) - .setParameter("uuid", uuid) - .uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, MedicationDispense.class, uuid); } @Override diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateObsDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateObsDAO.java index e7922983a204..d62719b3677d 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateObsDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateObsDAO.java @@ -9,22 +9,24 @@ */ package org.openmrs.api.db.hibernate; +import java.util.ArrayList; import java.util.Date; import java.util.List; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Subquery; + import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; import org.hibernate.FlushMode; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.DetachedCriteria; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Property; -import org.hibernate.criterion.Restrictions; -import org.hibernate.criterion.Subqueries; import org.openmrs.Concept; import org.openmrs.ConceptName; import org.openmrs.Encounter; @@ -99,18 +101,31 @@ public Obs saveObs(Obs obs) throws DAOException { * Integer, Integer, Date, Date, boolean, String) */ @Override - @SuppressWarnings("unchecked") public List getObservations(List whom, List encounters, List questions, List answers, List personTypes, List locations, List sortList, Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate, boolean includeVoidedObs, String accessionNumber) throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Obs.class); + Root root = cq.from(Obs.class); + + List predicates = createGetObservationsCriteria(cb, root, whom, encounters, questions, answers, personTypes, locations, + obsGroupId, fromDate, toDate, null, includeVoidedObs, accessionNumber); + + cq.where(predicates.toArray(new Predicate[]{})); + + cq.orderBy(createOrderList(cb, root, sortList)); + + TypedQuery query = session.createQuery(cq); - Criteria criteria = createGetObservationsCriteria(whom, encounters, questions, answers, personTypes, locations, - sortList, mostRecentN, obsGroupId, fromDate, toDate, null, includeVoidedObs, accessionNumber); + if (mostRecentN != null && mostRecentN > 0) { + query.setMaxResults(mostRecentN); + } - return criteria.list(); + return query.getResultList(); } - + /** * @see org.openmrs.api.db.ObsDAO#getObservationCount(List, List, List, List, List, List, Integer, Date, Date, List, boolean, String) */ @@ -119,131 +134,148 @@ public Long getObservationCount(List whom, List encounters, L List answers, List personTypes, List locations, Integer obsGroupId, Date fromDate, Date toDate, List valueCodedNameAnswers, boolean includeVoidedObs, String accessionNumber) throws DAOException { - Criteria criteria = createGetObservationsCriteria(whom, encounters, questions, answers, personTypes, locations, - null, null, obsGroupId, fromDate, toDate, valueCodedNameAnswers, includeVoidedObs, accessionNumber); - criteria.setProjection(Projections.rowCount()); - return (Long) criteria.list().get(0); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(Long.class); + Root root = criteriaQuery.from(Obs.class); + + criteriaQuery.select(cb.count(root)); + + List predicates = createGetObservationsCriteria(cb, root, whom, encounters, questions, answers, + personTypes, locations, obsGroupId, fromDate, toDate, + valueCodedNameAnswers, includeVoidedObs, accessionNumber); + + criteriaQuery.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(criteriaQuery).getSingleResult(); } /** * A utility method for creating a criteria based on parameters (which are optional) * + * @param cb + * @param root * @param whom * @param encounters * @param questions * @param answers * @param personTypes * @param locations - * @param sortList If a field needs to be in asc order, " asc" has to be appended to the field name. For example: fieldname asc - * @param mostRecentN * @param obsGroupId * @param fromDate * @param toDate * @param includeVoidedObs * @param accessionNumber - * @return + * @return a list of predicates that can form part of a query */ - private Criteria createGetObservationsCriteria(List whom, List encounters, List questions, - List answers, List personTypes, List locations, List sortList, - Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate, List valueCodedNameAnswers, + private List createGetObservationsCriteria(CriteriaBuilder cb, Root root, List whom, List encounters, List questions, + List answers, List personTypes, List locations, Integer obsGroupId, Date fromDate, Date toDate, List valueCodedNameAnswers, boolean includeVoidedObs, String accessionNumber) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Obs.class, "obs"); + List predicates = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(whom)) { - criteria.add(Restrictions.in("person", whom)); + predicates.add(root.get("person").in(whom)); } - + if (CollectionUtils.isNotEmpty(encounters)) { - criteria.add(Restrictions.in("encounter", encounters)); + predicates.add(root.get("encounter").in(encounters)); } - + if (CollectionUtils.isNotEmpty(questions)) { - criteria.add(Restrictions.in("concept", questions)); + predicates.add(root.get("concept").in(questions)); } - + if (CollectionUtils.isNotEmpty(answers)) { - criteria.add(Restrictions.in("valueCoded", answers)); + predicates.add(root.get("valueCoded").in(answers)); } - + if (CollectionUtils.isNotEmpty(personTypes)) { - getCriteriaPersonModifier(criteria, personTypes); + predicates.addAll(getCriteriaPersonModifier(cb, root, personTypes)); } - + if (CollectionUtils.isNotEmpty(locations)) { - criteria.add(Restrictions.in("location", locations)); + predicates.add(root.get("location").in(locations)); } - + + if (obsGroupId != null) { + predicates.add(cb.equal(root.get("obsGroup").get("obsId"), obsGroupId)); + } + + if (fromDate != null) { + predicates.add(cb.greaterThanOrEqualTo(root.get("obsDatetime"), fromDate)); + } + + if (toDate != null) { + predicates.add(cb.lessThanOrEqualTo(root.get("obsDatetime"), toDate)); + } + + if (CollectionUtils.isNotEmpty(valueCodedNameAnswers)) { + predicates.add(root.get("valueCodedName").in(valueCodedNameAnswers)); + } + + if (!includeVoidedObs) { + predicates.add(cb.isFalse(root.get("voided"))); + } + + if (accessionNumber != null) { + predicates.add(cb.equal(root.get("accessionNumber"), accessionNumber)); + } + + return predicates; + } + + private List createOrderList(CriteriaBuilder cb, Root root, List sortList) { + List orders = new ArrayList<>(); if (CollectionUtils.isNotEmpty(sortList)) { for (String sort : sortList) { if (StringUtils.isNotEmpty(sort)) { // Split the sort, the field name shouldn't contain space char, so it's safe String[] split = sort.split(" ", 2); String fieldName = split[0]; - + if (split.length == 2 && "asc".equals(split[1])) { /* If asc is specified */ - criteria.addOrder(Order.asc(fieldName)); + orders.add(cb.asc(root.get(fieldName))); } else { /* If the field hasn't got ordering or desc is specified */ - criteria.addOrder(Order.desc(fieldName)); + orders.add(cb.desc(root.get(fieldName))); } } } } - - if (mostRecentN != null && mostRecentN > 0) { - criteria.setMaxResults(mostRecentN); - } - - if (obsGroupId != null) { - criteria.createAlias("obsGroup", "og"); - criteria.add(Restrictions.eq("og.obsId", obsGroupId)); - } - - if (fromDate != null) { - criteria.add(Restrictions.ge("obsDatetime", fromDate)); - } - - if (toDate != null) { - criteria.add(Restrictions.le("obsDatetime", toDate)); - } - - if (CollectionUtils.isNotEmpty(valueCodedNameAnswers)) { - criteria.add(Restrictions.in("valueCodedName", valueCodedNameAnswers)); - } - - if (!includeVoidedObs) { - criteria.add(Restrictions.eq("voided", false)); - } - - if (accessionNumber != null) { - criteria.add(Restrictions.eq("accessionNumber", accessionNumber)); - } - - return criteria; + return orders; } - + /** - * Convenience method that adds an expression to the given criteria according to - * what types of person objects is wanted + * Convenience method that adds an expression to a list of predicates according to the types of person objects + * that are required. * - * @param criteria - * @param personType - * @return the given criteria (for chaining) + * @param cb instance of CriteriaBuilder + * @param root Root entity in the JPA criteria query + * @param personTypes list of person types as filters + * @return a list of javax.persistence.criteria.Predicate instances. */ - private Criteria getCriteriaPersonModifier(Criteria criteria, List personTypes) { + private List getCriteriaPersonModifier(CriteriaBuilder cb, Root root, List personTypes) { + List predicates = new ArrayList<>(); + if (personTypes.contains(PERSON_TYPE.PATIENT)) { - DetachedCriteria crit = DetachedCriteria.forClass(Patient.class, "patient").setProjection( - Property.forName("patientId")); - criteria.add(Subqueries.propertyIn("person.personId", crit)); + Subquery patientSubquery = cb.createQuery().subquery(Integer.class); + Root patientRoot = patientSubquery.from(Patient.class); + patientSubquery.select(patientRoot.get("patientId")); + + predicates.add(cb.in(root.get("person").get("personId")).value(patientSubquery)); } - + if (personTypes.contains(PERSON_TYPE.USER)) { - DetachedCriteria crit = DetachedCriteria.forClass(User.class, "user").setProjection(Property.forName("userId")); - criteria.add(Subqueries.propertyIn("person.personId", crit)); + Subquery userSubquery = cb.createQuery().subquery(Integer.class); + Root userRoot = userSubquery.from(User.class); + userSubquery.select(userRoot.get("userId")); + + predicates.add(cb.in(root.get("person").get("personId")).value(userSubquery)); } - - return criteria; + + return predicates; } /** @@ -251,8 +283,7 @@ private Criteria getCriteriaPersonModifier(Criteria criteria, List */ @Override public Obs getObsByUuid(String uuid) { - return (Obs) sessionFactory.getCurrentSession().createQuery("from Obs o where o.uuid = :uuid").setString("uuid", - uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Obs.class, uuid); } /** @@ -260,9 +291,14 @@ public Obs getObsByUuid(String uuid) { */ @Override public Obs getRevisionObs(Obs initialObs) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Obs.class, "obs"); - criteria.add(Restrictions.eq("previousVersion", initialObs)); - return (Obs) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Obs.class); + Root root = cq.from(Obs.class); + + cq.where(cb.equal(root.get("previousVersion"), initialObs)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -276,7 +312,7 @@ public Obs.Status getSavedStatus(Obs obs) { session.setHibernateFlushMode(FlushMode.MANUAL); try { SQLQuery sql = session.createSQLQuery("select status from obs where obs_id = :obsId"); - sql.setInteger("obsId", obs.getObsId()); + sql.setParameter("obsId", obs.getObsId()); return Obs.Status.valueOf((String) sql.uniqueResult()); } finally { diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsDataDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsDataDAO.java index 1c178f44bed1..e0a38a79e312 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsDataDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsDataDAO.java @@ -11,9 +11,14 @@ import java.util.List; -import org.hibernate.Criteria; -import org.hibernate.Query; -import org.hibernate.criterion.Restrictions; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; import org.openmrs.BaseOpenmrsData; import org.openmrs.api.db.OpenmrsDataDAO; @@ -36,13 +41,16 @@ public HibernateOpenmrsDataDAO(Class mappedClass) { */ @Override public List getAll(boolean includeVoided) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(mappedClass); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(mappedClass); + Root root = cq.from(mappedClass); + if (!includeVoided) { - crit.add(Restrictions.eq("voided", false)); + cq.where(cb.isFalse(root.get("voided"))); } - - return crit.list(); + + return session.createQuery(cq).getResultList(); } /** @@ -50,16 +58,23 @@ public List getAll(boolean includeVoided) { */ @Override public List getAll(boolean includeVoided, Integer firstResult, Integer maxResults) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(mappedClass); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(mappedClass); + Root root = cq.from(mappedClass); + if (!includeVoided) { - crit.add(Restrictions.eq("voided", false)); + cq.where(cb.isFalse(root.get("voided"))); } - crit.setFirstResult(firstResult); - crit.setMaxResults(maxResults); - - return crit.list(); - + + TypedQuery query = session.createQuery(cq); + if (firstResult != null) { + query.setFirstResult(firstResult); + } + if (maxResults != null) { + query.setMaxResults(maxResults); + } + return query.getResultList(); } /** @@ -75,9 +90,8 @@ public int getAllCount(boolean includeVoided) { } Query query = sessionFactory.getCurrentSession().createQuery(hql); - Number count = (Number) query.uniqueResult(); + Number count = JpaUtils.getSingleResultOrNull(query); return count == null ? 0 : count.intValue(); } - } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsMetadataDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsMetadataDAO.java index 33a8e87517ef..abb696e63a9c 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsMetadataDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsMetadataDAO.java @@ -11,9 +11,13 @@ import java.util.List; -import org.hibernate.Criteria; -import org.hibernate.Query; -import org.hibernate.criterion.Restrictions; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.hibernate.Session; import org.openmrs.BaseOpenmrsMetadata; import org.openmrs.api.db.OpenmrsMetadataDAO; @@ -36,32 +40,44 @@ public HibernateOpenmrsMetadataDAO(Class mappedClass) { */ @Override public List getAll(boolean includeRetired) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(mappedClass); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(mappedClass); + Root root = cq.from(mappedClass); + if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - return crit.list(); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.OpenmrsMetadataDAO#getAll(boolean, java.lang.Integer, java.lang.Integer) */ @Override public List getAll(boolean includeRetired, Integer firstResult, Integer maxResults) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(mappedClass); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(mappedClass); + Root root = cq.from(mappedClass); + if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); + } + + TypedQuery query = session.createQuery(cq); + if (firstResult != null) { + query.setFirstResult(firstResult); + } + if (maxResults != null) { + query.setMaxResults(maxResults); } - crit.setFirstResult(firstResult); - crit.setMaxResults(maxResults); - - return crit.list(); + return query.getResultList(); } - + + /** * @see org.openmrs.api.db.OpenmrsMetadataDAO#getAllCount(boolean) */ @@ -75,7 +91,7 @@ public int getAllCount(boolean includeRetired) { } Query query = sessionFactory.getCurrentSession().createQuery(hql); - Number count = (Number) query.uniqueResult(); + Number count = JpaUtils.getSingleResultOrNull(query); return count == null ? 0 : count.intValue(); } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsObjectDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsObjectDAO.java index 1901fd78e550..c0a786b18a6f 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsObjectDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOpenmrsObjectDAO.java @@ -11,9 +11,7 @@ import java.io.Serializable; -import org.hibernate.Criteria; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Restrictions; import org.openmrs.BaseOpenmrsObject; import org.openmrs.api.db.OpenmrsObjectDAO; import org.springframework.beans.factory.annotation.Autowired; @@ -45,10 +43,10 @@ public T getById(Serializable id) { */ @Override public T getByUuid(String uuid) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(mappedClass); - return (T) crit.add(Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, mappedClass, uuid); } - + + /** * @see org.openmrs.api.db.OpenmrsObjectDAO#delete(org.openmrs.BaseOpenmrsObject) */ diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOrderDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOrderDAO.java index 9104ab2776bc..7769381287db 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOrderDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOrderDAO.java @@ -10,20 +10,13 @@ package org.openmrs.api.db.hibernate; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; -import org.hibernate.FlushMode; import org.hibernate.LockOptions; -import org.hibernate.Query; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Criterion; -import org.hibernate.criterion.Disjunction; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Restrictions; -import org.hibernate.criterion.SimpleExpression; -import org.hibernate.transform.DistinctRootEntityResultTransformer; import org.openmrs.Concept; -import org.openmrs.ConceptClass; import org.openmrs.CareSetting; +import org.openmrs.ConceptClass; +import org.openmrs.ConceptName; import org.openmrs.Encounter; import org.openmrs.GlobalProperty; import org.openmrs.Order; @@ -45,6 +38,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.persistence.FlushModeType; +import javax.persistence.Query; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import java.util.ArrayList; @@ -114,7 +114,7 @@ public void deleteOrder(Order order) throws DAOException { public Order getOrder(Integer orderId) throws DAOException { log.debug("getting order #{}", orderId); - return (Order) sessionFactory.getCurrentSession().get(Order.class, orderId); + return sessionFactory.getCurrentSession().get(Order.class, orderId); } /** @@ -122,37 +122,42 @@ public Order getOrder(Integer orderId) throws DAOException { * java.util.List, java.util.List, java.util.List) */ @Override - public List getOrders(OrderType orderType, List patients, List concepts, List orderers, - List encounters) { - - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Order.class); - + public List getOrders(OrderType orderType, List patients, List concepts, List orderers, List encounters) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Order.class); + Root root = cq.from(Order.class); + + List predicates = new ArrayList<>(); + if (orderType != null) { - crit.add(Restrictions.eq("orderType", orderType)); + predicates.add(cb.equal(root.get("orderType"), orderType)); } - + if (!patients.isEmpty()) { - crit.add(Restrictions.in("patient", patients)); + predicates.add(root.get("patient").in(patients)); } - + if (!concepts.isEmpty()) { - crit.add(Restrictions.in("concept", concepts)); + predicates.add(root.get("concept").in(concepts)); } - + // we are not checking the other status's here because they are // algorithm dependent - + if (!orderers.isEmpty()) { - crit.add(Restrictions.in("orderer", orderers)); + predicates.add(root.get("orderer").in(orderers)); } - + if (!encounters.isEmpty()) { - crit.add(Restrictions.in("encounter", encounters)); + predicates.add(root.get("encounter").in(encounters)); } - - crit.addOrder(org.hibernate.criterion.Order.desc("dateActivated")); - - return crit.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + cq.orderBy(cb.desc(root.get("dateActivated"))); + + return session.createQuery(cq).getResultList(); } /** @@ -160,107 +165,111 @@ public List getOrders(OrderType orderType, List patients, List getOrders(OrderSearchCriteria searchCriteria) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Order.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Order.class); + Root root = cq.from(Order.class); + + List predicates = new ArrayList<>(); + if (searchCriteria.getPatient() != null && searchCriteria.getPatient().getPatientId() != null) { - crit.add(Restrictions.eq("patient", searchCriteria.getPatient())); + predicates.add(cb.equal(root.get("patient"), searchCriteria.getPatient())); } if (searchCriteria.getCareSetting() != null && searchCriteria.getCareSetting().getId() != null) { - crit.add(Restrictions.eq("careSetting", searchCriteria.getCareSetting())); + predicates.add(cb.equal(root.get("careSetting"), searchCriteria.getCareSetting())); } if (searchCriteria.getConcepts() != null && !searchCriteria.getConcepts().isEmpty()) { - crit.add(Restrictions.in("concept", searchCriteria.getConcepts())); + predicates.add(root.get("concept").in(searchCriteria.getConcepts())); } if (searchCriteria.getOrderTypes() != null && !searchCriteria.getOrderTypes().isEmpty()) { - crit.add(Restrictions.in("orderType", searchCriteria.getOrderTypes())); + predicates.add(root.get("orderType").in(searchCriteria.getOrderTypes())); } if (searchCriteria.getOrderNumber() != null) { - crit.add(Restrictions.eq("orderNumber", searchCriteria.getOrderNumber()).ignoreCase()); + predicates.add(cb.equal(cb.lower(root.get("orderNumber")), searchCriteria.getOrderNumber().toLowerCase())); } if (searchCriteria.getAccessionNumber() != null) { - crit.add(Restrictions.eq("accessionNumber", searchCriteria.getAccessionNumber()).ignoreCase()); + predicates.add(cb.equal(cb.lower(root.get("accessionNumber")), searchCriteria.getAccessionNumber().toLowerCase())); } if (searchCriteria.getActivatedOnOrBeforeDate() != null) { // set the date's time to the last millisecond of the date Calendar cal = Calendar.getInstance(); cal.setTime(searchCriteria.getActivatedOnOrBeforeDate()); - crit.add(Restrictions.le("dateActivated", OpenmrsUtil.getLastMomentOfDay(cal.getTime()))); + predicates.add(cb.lessThanOrEqualTo(root.get("dateActivated"), OpenmrsUtil.getLastMomentOfDay(cal.getTime()))); } if (searchCriteria.getActivatedOnOrAfterDate() != null) { // set the date's time to 00:00:00.000 Calendar cal = Calendar.getInstance(); cal.setTime(searchCriteria.getActivatedOnOrAfterDate()); - crit.add(Restrictions.ge("dateActivated", OpenmrsUtil.firstSecondOfDay(cal.getTime()))); + predicates.add(cb.greaterThanOrEqualTo(root.get("dateActivated"), OpenmrsUtil.firstSecondOfDay(cal.getTime()))); } if (searchCriteria.isStopped()) { // an order is considered Canceled regardless of the time when the dateStopped was set - crit.add(Restrictions.isNotNull("dateStopped")); + predicates.add(cb.isNotNull(root.get("dateStopped"))); } if (searchCriteria.getAutoExpireOnOrBeforeDate() != null) { // set the date's time to the last millisecond of the date Calendar cal = Calendar.getInstance(); cal.setTime(searchCriteria.getAutoExpireOnOrBeforeDate()); - crit.add(Restrictions.le("autoExpireDate", OpenmrsUtil.getLastMomentOfDay(cal.getTime()))); - } - if (searchCriteria.getAction() != null) { - crit.add(Restrictions.eq("action", searchCriteria.getAction())); - } - if (searchCriteria.getExcludeDiscontinueOrders()) { - crit.add(Restrictions.or( - Restrictions.ne("action", Order.Action.DISCONTINUE), - Restrictions.isNull("action"))); - } - SimpleExpression fulfillerStatusExpr = null; - if (searchCriteria.getFulfillerStatus() != null) { - fulfillerStatusExpr = Restrictions.eq("fulfillerStatus", searchCriteria.getFulfillerStatus()); - } - Criterion fulfillerStatusCriteria = null; - if (searchCriteria.getIncludeNullFulfillerStatus() != null ) { - if (searchCriteria.getIncludeNullFulfillerStatus().booleanValue()) { - fulfillerStatusCriteria = Restrictions.isNull("fulfillerStatus"); - } else { - fulfillerStatusCriteria = Restrictions.isNotNull("fulfillerStatus"); - } - } - - if (fulfillerStatusExpr != null && fulfillerStatusCriteria != null) { - crit.add(Restrictions.or(fulfillerStatusExpr, fulfillerStatusCriteria)); - } else if (fulfillerStatusExpr != null) { - crit.add(fulfillerStatusExpr); - } else if ( fulfillerStatusCriteria != null ){ - crit.add(fulfillerStatusCriteria); - } + predicates.add(cb.lessThanOrEqualTo(root.get("autoExpireDate"), OpenmrsUtil.getLastMomentOfDay(cal.getTime()))); + } + if (searchCriteria.getAction() != null) { + predicates.add(cb.equal(root.get("action"), searchCriteria.getAction())); + } + if (searchCriteria.getExcludeDiscontinueOrders()) { + predicates.add(cb.or( + cb.notEqual(root.get("action"), Order.Action.DISCONTINUE), + cb.isNull(root.get("action")))); + } + Predicate fulfillerStatusExpr = null; + if (searchCriteria.getFulfillerStatus() != null) { + fulfillerStatusExpr = cb.equal(root.get("fulfillerStatus"), searchCriteria.getFulfillerStatus()); + } + + Predicate fulfillerStatusCriteria = null; + if (searchCriteria.getIncludeNullFulfillerStatus() != null ) { + if (searchCriteria.getIncludeNullFulfillerStatus()) { + fulfillerStatusCriteria = cb.isNull(root.get("fulfillerStatus")); + } else { + fulfillerStatusCriteria = cb.isNotNull(root.get("fulfillerStatus")); + } + } + + if (fulfillerStatusExpr != null && fulfillerStatusCriteria != null) { + predicates.add(cb.or(fulfillerStatusExpr, fulfillerStatusCriteria)); + } else if (fulfillerStatusExpr != null) { + predicates.add(fulfillerStatusExpr); + } else if ( fulfillerStatusCriteria != null ){ + predicates.add(fulfillerStatusCriteria); + } + if (searchCriteria.getExcludeCanceledAndExpired()) { Calendar cal = Calendar.getInstance(); // exclude expired orders (include only orders with autoExpireDate = null or autoExpireDate in the future) - crit.add(Restrictions.or( - Restrictions.isNull("autoExpireDate"), - Restrictions.gt("autoExpireDate", cal.getTime()))); + predicates.add(cb.or( + cb.isNull(root.get("autoExpireDate")), + cb.greaterThan(root.get("autoExpireDate"), cal.getTime()))); // exclude Canceled Orders - crit.add(Restrictions.or( - Restrictions.isNull("dateStopped"), - Restrictions.gt("dateStopped", cal.getTime()))); + predicates.add(cb.or( + cb.isNull(root.get("dateStopped")), + cb.greaterThan(root.get("dateStopped"), cal.getTime()))); } if (searchCriteria.getCanceledOrExpiredOnOrBeforeDate() != null) { // set the date's time to the last millisecond of the date Calendar cal = Calendar.getInstance(); cal.setTime(searchCriteria.getCanceledOrExpiredOnOrBeforeDate()); - crit.add(Restrictions.or( - Restrictions.and( - Restrictions.isNotNull("dateStopped"), - Restrictions.le("dateStopped", OpenmrsUtil.getLastMomentOfDay(cal.getTime()))), - Restrictions.and( - Restrictions.isNotNull("autoExpireDate"), - Restrictions.le("autoExpireDate", OpenmrsUtil.getLastMomentOfDay(cal.getTime()))))); + predicates.add(cb.or( + cb.and(cb.isNotNull(root.get("dateStopped")), cb.lessThanOrEqualTo(root.get("dateStopped"), OpenmrsUtil.getLastMomentOfDay(cal.getTime()))), + cb.and(cb.isNotNull(root.get("autoExpireDate")), cb.lessThanOrEqualTo(root.get("autoExpireDate"), OpenmrsUtil.getLastMomentOfDay(cal.getTime()))))); } if (!searchCriteria.getIncludeVoided()) { - crit.add(Restrictions.eq("voided", false)); + predicates.add(cb.isFalse(root.get("voided"))); } - crit.addOrder(org.hibernate.criterion.Order.desc("dateActivated")); - - return crit.list(); + cq.where(predicates.toArray(new Predicate[]{})); + cq.orderBy(cb.desc(root.get("dateActivated"))); + + return session.createQuery(cq).getResultList(); } /** @@ -268,9 +277,18 @@ public List getOrders(OrderSearchCriteria searchCriteria) { * boolean, boolean) */ @Override - public List getOrders(Patient patient, CareSetting careSetting, List orderTypes, - boolean includeVoided, boolean includeDiscontinuationOrders) { - return createOrderCriteria(patient, careSetting, orderTypes, includeVoided, includeDiscontinuationOrders).list(); + public List getOrders(Patient patient, CareSetting careSetting, List orderTypes, boolean includeVoided, + boolean includeDiscontinuationOrders) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Order.class); + Root root = cq.from(Order.class); + + List predicates = createOrderCriteria(cb, root, patient, careSetting, orderTypes, includeVoided, includeDiscontinuationOrders); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } /** @@ -278,8 +296,7 @@ public List getOrders(Patient patient, CareSetting careSetting, List cq = cb.createQuery(Order.class); + Root root = cq.from(Order.class); - return (Order) sessionFactory.getCurrentSession().createCriteria(Order.class).add( - Restrictions.eq("previousOrder", order)).add(Restrictions.eq("action", Order.Action.DISCONTINUE)).add( - Restrictions.eq("voided", false)).uniqueResult(); + cq.where( + cb.equal(root.get("previousOrder"), order), + cb.equal(root.get("action"), Order.Action.DISCONTINUE), + cb.isFalse(root.get("voided")) + ); + + return session.createQuery(cq).uniqueResult(); } @Override public Order getRevisionOrder(Order order) throws APIException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Order.class); - criteria.add(Restrictions.eq("previousOrder", order)).add(Restrictions.eq("action", Order.Action.REVISE)).add( - Restrictions.eq("voided", false)); - return (Order) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Order.class); + Root root = cq.from(Order.class); + + cq.where( + cb.equal(root.get("previousOrder"), order), + cb.equal(root.get("action"), Order.Action.REVISE), + cb.isFalse(root.get("voided")) + ); + + return session.createQuery(cq).uniqueResult(); } @Override @@ -312,10 +345,10 @@ public List getOrderFromDatabase(Order order, boolean isOrderADrugOrde Query query = sessionFactory.getCurrentSession().createSQLQuery(sql); query.setParameter("orderId", order.getOrderId()); - //prevent hibernate from flushing before fetching the list - query.setHibernateFlushMode(FlushMode.MANUAL); + //prevent jpa from flushing before fetching the list + query.setFlushMode(FlushModeType.COMMIT); - return query.list(); + return query.getResultList(); } /** @@ -333,8 +366,7 @@ public OrderGroup saveOrderGroup(OrderGroup orderGroup) throws DAOException { */ @Override public OrderGroup getOrderGroupByUuid(String uuid) throws DAOException { - return (OrderGroup) sessionFactory.getCurrentSession().createQuery("from OrderGroup o where o.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderGroup.class, uuid); } /** @@ -343,7 +375,7 @@ public OrderGroup getOrderGroupByUuid(String uuid) throws DAOException { */ @Override public OrderGroup getOrderGroupById(Integer orderGroupId) throws DAOException { - return (OrderGroup) sessionFactory.getCurrentSession().get(OrderGroup.class, orderGroupId); + return sessionFactory.getCurrentSession().get(OrderGroup.class, orderGroupId); } /** @@ -362,9 +394,14 @@ public void deleteObsThatReference(Order order) { */ @Override public Order getOrderByOrderNumber(String orderNumber) { - Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Order.class, "order"); - searchCriteria.add(Restrictions.eq("order.orderNumber", orderNumber)); - return (Order) searchCriteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Order.class); + Root root = cq.from(Order.class); + + cq.where(cb.equal(root.get("orderNumber"), orderNumber)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -372,7 +409,7 @@ public Order getOrderByOrderNumber(String orderNumber) { */ @Override public Long getNextOrderNumberSeedSequenceValue() { - GlobalProperty globalProperty = (GlobalProperty) sessionFactory.getCurrentSession().get(GlobalProperty.class, + GlobalProperty globalProperty = sessionFactory.getCurrentSession().get(GlobalProperty.class, OpenmrsConstants.GP_NEXT_ORDER_NUMBER_SEED, LockOptions.UPGRADE); if (globalProperty == null) { @@ -406,30 +443,34 @@ public Long getNextOrderNumberSeedSequenceValue() { * org.openmrs.CareSetting, java.util.Date) */ @Override - @SuppressWarnings("unchecked") public List getActiveOrders(Patient patient, List orderTypes, CareSetting careSetting, Date asOfDate) { - Criteria crit = createOrderCriteria(patient, careSetting, orderTypes, false, false); - crit.add(Restrictions.le("dateActivated", asOfDate)); - - Disjunction dateStoppedAndAutoExpDateDisjunction = Restrictions.disjunction(); - Criterion stopAndAutoExpDateAreBothNull = Restrictions.and(Restrictions.isNull("dateStopped"), Restrictions - .isNull("autoExpireDate")); - dateStoppedAndAutoExpDateDisjunction.add(stopAndAutoExpDateAreBothNull); - - Criterion autoExpireDateEqualToOrAfterAsOfDate = Restrictions.and(Restrictions.isNull("dateStopped"), Restrictions - .ge("autoExpireDate", asOfDate)); - dateStoppedAndAutoExpDateDisjunction.add(autoExpireDateEqualToOrAfterAsOfDate); - - dateStoppedAndAutoExpDateDisjunction.add(Restrictions.ge("dateStopped", asOfDate)); - - crit.add(dateStoppedAndAutoExpDateDisjunction); - - return crit.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Order.class); + Root root = cq.from(Order.class); + + List predicates = createOrderCriteria(cb, root, patient, careSetting, orderTypes, false, false); + + predicates.add(cb.lessThanOrEqualTo(root.get("dateActivated"), asOfDate)); + + Predicate dateStoppedAndAutoExpDateCondition = cb.or( + cb.and(cb.isNull(root.get("dateStopped")), cb.isNull(root.get("autoExpireDate"))), + cb.and(cb.isNull(root.get("dateStopped")), cb.greaterThanOrEqualTo(root.get("autoExpireDate"), asOfDate)), + cb.greaterThanOrEqualTo(root.get("dateStopped"), asOfDate) + ); + + predicates.add(dateStoppedAndAutoExpDateCondition); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } /** - * Creates and returns a Criteria Object filtering on the specified parameters + * Creates and returns a list of predicates filtering on the specified parameters * + * @param cb + * @param root * @param patient * @param careSetting * @param orderTypes @@ -437,26 +478,28 @@ public List getActiveOrders(Patient patient, List orderTypes, * @param includeDiscontinuationOrders * @return */ - private Criteria createOrderCriteria(Patient patient, CareSetting careSetting, List orderTypes, - boolean includeVoided, boolean includeDiscontinuationOrders) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Order.class); + private List createOrderCriteria(CriteriaBuilder cb, Root root, Patient patient, + CareSetting careSetting, List orderTypes, boolean includeVoided, + boolean includeDiscontinuationOrders) { + List predicates = new ArrayList<>(); + if (patient != null) { - criteria.add(Restrictions.eq("patient", patient)); + predicates.add(cb.equal(root.get("patient"), patient)); } if (careSetting != null) { - criteria.add(Restrictions.eq("careSetting", careSetting)); + predicates.add(cb.equal(root.get("careSetting"), careSetting)); } if (orderTypes != null && !orderTypes.isEmpty()) { - criteria.add(Restrictions.in("orderType", orderTypes)); + predicates.add(root.get("orderType").in(orderTypes)); } if (!includeVoided) { - criteria.add(Restrictions.eq("voided", false)); + predicates.add(cb.isFalse(root.get("voided"))); } if (!includeDiscontinuationOrders) { - criteria.add(Restrictions.ne("action", Order.Action.DISCONTINUE)); + predicates.add(cb.notEqual(root.get("action"), Order.Action.DISCONTINUE)); } - - return criteria; + + return predicates; } /** @@ -464,7 +507,7 @@ private Criteria createOrderCriteria(Patient patient, CareSetting careSetting, L */ @Override public CareSetting getCareSetting(Integer careSettingId) { - return (CareSetting) sessionFactory.getCurrentSession().get(CareSetting.class, careSettingId); + return sessionFactory.getCurrentSession().get(CareSetting.class, careSettingId); } /** @@ -472,8 +515,7 @@ public CareSetting getCareSetting(Integer careSettingId) { */ @Override public CareSetting getCareSettingByUuid(String uuid) { - return (CareSetting) sessionFactory.getCurrentSession().createQuery("from CareSetting cs where cs.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, CareSetting.class, uuid); } /** @@ -481,8 +523,14 @@ public CareSetting getCareSettingByUuid(String uuid) { */ @Override public CareSetting getCareSettingByName(String name) { - return (CareSetting) sessionFactory.getCurrentSession().createCriteria(CareSetting.class).add( - Restrictions.ilike("name", name)).uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(CareSetting.class); + Root root = cq.from(CareSetting.class); + + cq.where(cb.like(cb.lower(root.get("name")), name.toLowerCase())); + + return session.createQuery(cq).uniqueResult(); } /** @@ -490,11 +538,16 @@ public CareSetting getCareSettingByName(String name) { */ @Override public List getCareSettings(boolean includeRetired) { - Criteria c = sessionFactory.getCurrentSession().createCriteria(CareSetting.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(CareSetting.class); + Root root = cq.from(CareSetting.class); + if (!includeRetired) { - c.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - return c.list(); + + return session.createQuery(cq).getResultList(); } /** @@ -502,9 +555,14 @@ public List getCareSettings(boolean includeRetired) { */ @Override public OrderType getOrderTypeByName(String orderTypeName) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(OrderType.class); - criteria.add(Restrictions.eq("name", orderTypeName)); - return (OrderType) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderType.class); + Root root = cq.from(OrderType.class); + + cq.where(cb.equal(root.get("name"), orderTypeName)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -512,7 +570,7 @@ public OrderType getOrderTypeByName(String orderTypeName) { */ @Override public OrderFrequency getOrderFrequency(Integer orderFrequencyId) { - return (OrderFrequency) sessionFactory.getCurrentSession().get(OrderFrequency.class, orderFrequencyId); + return sessionFactory.getCurrentSession().get(OrderFrequency.class, orderFrequencyId); } /** @@ -520,8 +578,7 @@ public OrderFrequency getOrderFrequency(Integer orderFrequencyId) { */ @Override public OrderFrequency getOrderFrequencyByUuid(String uuid) { - return (OrderFrequency) sessionFactory.getCurrentSession().createQuery("from OrderFrequency o where o.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderFrequency.class, uuid); } /** @@ -529,11 +586,16 @@ public OrderFrequency getOrderFrequencyByUuid(String uuid) { */ @Override public List getOrderFrequencies(boolean includeRetired) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(OrderFrequency.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderFrequency.class); + Root root = cq.from(OrderFrequency.class); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - return criteria.list(); + + return session.createQuery(cq).getResultList(); } /** @@ -542,14 +604,19 @@ public List getOrderFrequencies(boolean includeRetired) { @Override public List getOrderFrequencies(String searchPhrase, Locale locale, boolean exactLocale, boolean includeRetired) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderFrequency.class); + Root root = cq.from(OrderFrequency.class); + + Join conceptJoin = root.join("concept"); + Join conceptNameJoin = conceptJoin.join("names"); + + List predicates = new ArrayList<>(); - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(OrderFrequency.class, "orderFreq"); - criteria.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE); - - //match on the concept names of the concepts - criteria.createAlias("orderFreq.concept", "concept"); - criteria.createAlias("concept.names", "conceptName"); - criteria.add(Restrictions.ilike("conceptName.name", searchPhrase, MatchMode.ANYWHERE)); + Predicate searchPhrasePredicate = cb.like(cb.lower(conceptNameJoin.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(searchPhrase)); + predicates.add(searchPhrasePredicate); + if (locale != null) { List locales = new ArrayList<>(2); locales.add(locale); @@ -557,14 +624,14 @@ public List getOrderFrequencies(String searchPhrase, Locale loca if (!exactLocale && StringUtils.isNotBlank(locale.getCountry())) { locales.add(new Locale(locale.getLanguage())); } - criteria.add(Restrictions.in("conceptName.locale", locales)); + predicates.add(conceptNameJoin.get("locale").in(locales)); } - if (!includeRetired) { - criteria.add(Restrictions.eq("orderFreq.retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } - - return criteria.list(); + cq.where(predicates.toArray(new Predicate[]{})).distinct(true); + + return session.createQuery(cq).list(); } /** @@ -589,7 +656,8 @@ public void purgeOrderFrequency(OrderFrequency orderFrequency) { */ @Override public boolean isOrderFrequencyInUse(OrderFrequency orderFrequency) { - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); Set> entities = sessionFactory.getMetamodel().getEntities(); for (EntityType entityTpe : entities) { @@ -606,16 +674,19 @@ public boolean isOrderFrequencyInUse(OrderFrequency orderFrequency) { for (Attribute attribute : entityTpe.getDeclaredAttributes()) { if (attribute.getJavaType().equals(OrderFrequency.class)) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(entityClass); - criteria.add(Restrictions.eq(attribute.getName(), orderFrequency)); - criteria.setMaxResults(1); - if (!criteria.list().isEmpty()) { + CriteriaQuery cq = cb.createQuery(entityClass); + Root root = cq.from(entityClass); + cq.where(cb.equal(root.get(attribute.getName()), orderFrequency)); + cq.distinct(true); + + Query query = session.createQuery(cq); + query.setMaxResults(1); + if (!query.getResultList().isEmpty()) { return true; } } } } - return false; } @@ -624,9 +695,14 @@ public boolean isOrderFrequencyInUse(OrderFrequency orderFrequency) { */ @Override public OrderFrequency getOrderFrequencyByConcept(Concept concept) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(OrderFrequency.class); - criteria.add(Restrictions.eq("concept", concept)); - return (OrderFrequency) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderFrequency.class); + Root root = cq.from(OrderFrequency.class); + + cq.where(cb.equal(root.get("concept"), concept)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -634,9 +710,14 @@ public OrderFrequency getOrderFrequencyByConcept(Concept concept) { */ @Override public OrderType getOrderType(Integer orderTypeId) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(OrderType.class); - criteria.add(Restrictions.eq("orderTypeId", orderTypeId)); - return (OrderType) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderType.class); + Root root = cq.from(OrderType.class); + + cq.where(cb.equal(root.get("orderTypeId"), orderTypeId)); + + return session.createQuery(cq).uniqueResult(); } /** @@ -644,8 +725,7 @@ public OrderType getOrderType(Integer orderTypeId) { */ @Override public OrderType getOrderTypeByUuid(String uuid) { - return (OrderType) sessionFactory.getCurrentSession().createQuery("from OrderType o where o.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderType.class, uuid); } /** @@ -653,11 +733,16 @@ public OrderType getOrderTypeByUuid(String uuid) { */ @Override public List getOrderTypes(boolean includeRetired) { - Criteria c = sessionFactory.getCurrentSession().createCriteria(OrderType.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderType.class); + Root root = cq.from(OrderType.class); + if (!includeRetired) { - c.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - return c.list(); + + return session.createQuery(cq).getResultList(); } /** @@ -692,53 +777,83 @@ public void purgeOrderType(OrderType orderType) { */ @Override public List getOrderSubtypes(OrderType orderType, boolean includeRetired) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(OrderType.class); - criteria.add(Restrictions.eq("parent", orderType)); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderType.class); + Root root = cq.from(OrderType.class); + + List predicates = new ArrayList<>(); if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } - return criteria.list(); + predicates.add(cb.equal(root.get("parent"), orderType)); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } @Override public boolean isOrderTypeInUse(OrderType orderType) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Order.class); - criteria.add(Restrictions.eq("orderType", orderType)); - return !criteria.list().isEmpty(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Order.class); + Root root = cq.from(Order.class); + + cq.where(cb.equal(root.get("orderType"), orderType)); + + return !session.createQuery(cq).getResultList().isEmpty(); } + /** * @see OrderDAO#getOrderGroupsByPatient(Patient) */ @Override - public List getOrderGroupsByPatient(Patient patient) throws DAOException { + public List getOrderGroupsByPatient(Patient patient) { if (patient == null) { throw new APIException("Patient cannot be null"); } - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(OrderGroup.class); - criteria.add(Restrictions.eq("patient", patient)); - return criteria.list(); + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderGroup.class); + Root root = cq.from(OrderGroup.class); + + cq.where(cb.equal(root.get("patient"), patient)); + + return session.createQuery(cq).getResultList(); } /** * @see OrderDAO#getOrderGroupsByEncounter(Encounter) */ @Override - public List getOrderGroupsByEncounter(Encounter encounter) throws DAOException { + public List getOrderGroupsByEncounter(Encounter encounter) { if (encounter == null) { throw new APIException("Encounter cannot be null"); } - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(OrderGroup.class); - criteria.add(Restrictions.eq("encounter", encounter)); - return criteria.list(); + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderGroup.class); + Root root = cq.from(OrderGroup.class); + + cq.where(cb.equal(root.get("encounter"), encounter)); + + return session.createQuery(cq).getResultList(); } /** * @see org.openmrs.api.db.OrderDAO#getAllOrderGroupAttributeTypes() */ - @SuppressWarnings("unchecked") @Override - public List getAllOrderGroupAttributeTypes() throws DAOException{ - return sessionFactory.getCurrentSession().createCriteria(OrderGroupAttributeType.class).list(); + public List getAllOrderGroupAttributeTypes() { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderGroupAttributeType.class); + cq.from(OrderGroupAttributeType.class); + + return session.createQuery(cq).getResultList(); } /** @@ -754,8 +869,7 @@ public OrderGroupAttributeType getOrderGroupAttributeType(Integer orderGroupAttr */ @Override public OrderGroupAttributeType getOrderGroupAttributeTypeByUuid(String uuid) throws DAOException{ - return (OrderGroupAttributeType) sessionFactory.getCurrentSession().createCriteria(OrderGroupAttributeType.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderGroupAttributeType.class, uuid); } /** @@ -780,37 +894,45 @@ public void deleteOrderGroupAttributeType(OrderGroupAttributeType orderGroupAttr */ @Override public OrderGroupAttribute getOrderGroupAttributeByUuid(String uuid) throws DAOException{ - return (OrderGroupAttribute) sessionFactory.getCurrentSession().createQuery("from OrderGroupAttribute d where d.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderGroupAttribute.class, uuid); } /** * @see org.openmrs.api.db.OrderDAO#getOrderGroupAttributeTypeByName(String) */ @Override - public OrderGroupAttributeType getOrderGroupAttributeTypeByName(String name) throws DAOException{ - return (OrderGroupAttributeType) sessionFactory.getCurrentSession().createCriteria(OrderGroupAttributeType.class).add( - Restrictions.eq("name", name)).uniqueResult(); + public OrderGroupAttributeType getOrderGroupAttributeTypeByName(String name) throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderGroupAttributeType.class); + Root root = cq.from(OrderGroupAttributeType.class); + + cq.where(cb.equal(root.get("name"), name)); + + return session.createQuery(cq).uniqueResult(); } + /** * @param uuid The uuid associated with the order attribute to retrieve. * @see org.openmrs.api.db.OrderDAO#getOrderAttributeByUuid(String) */ @Override public OrderAttribute getOrderAttributeByUuid(String uuid) throws DAOException { - return (OrderAttribute) sessionFactory.getCurrentSession() - .createQuery("from OrderAttribute a where a.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderAttribute.class, uuid); } /** * @see org.openmrs.api.db.OrderDAO#getAllOrderAttributeTypes() */ - @SuppressWarnings("unchecked") @Override public List getAllOrderAttributeTypes() throws DAOException { - return sessionFactory.getCurrentSession().createCriteria(OrderAttributeType.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderAttributeType.class); + cq.from(OrderAttributeType.class); + + return session.createQuery(cq).getResultList(); } /** @@ -828,8 +950,7 @@ public OrderAttributeType getOrderAttributeTypeById(Integer orderAttributeTypeId */ @Override public OrderAttributeType getOrderAttributeTypeByUuid(String uuid) throws DAOException { - return (OrderAttributeType) sessionFactory.getCurrentSession().createCriteria(OrderAttributeType.class) - .add(Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderAttributeType.class, uuid); } /** @@ -857,7 +978,13 @@ public void deleteOrderAttributeType(OrderAttributeType orderAttributeType) thro */ @Override public OrderAttributeType getOrderAttributeTypeByName(String name) throws DAOException { - return (OrderAttributeType) sessionFactory.getCurrentSession().createCriteria(OrderAttributeType.class) - .add(Restrictions.eq("name", name)).uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderAttributeType.class); + Root root = cq.from(OrderAttributeType.class); + + cq.where(cb.equal(root.get("name"), name)); + + return session.createQuery(cq).uniqueResult(); } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOrderSetDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOrderSetDAO.java index ce04eb044815..010c2697a404 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOrderSetDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateOrderSetDAO.java @@ -9,12 +9,13 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; import java.util.List; -import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Restrictions; import org.openmrs.OrderSet; import org.openmrs.OrderSetAttribute; import org.openmrs.OrderSetAttributeType; @@ -68,20 +69,25 @@ public OrderSet save(OrderSet orderSet) throws DAOException { */ @Override public List getOrderSets(boolean includeRetired) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(OrderSet.class, "orderSet"); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderSet.class); + Root root = cq.from(OrderSet.class); + if (!includeRetired) { - crit.add(Restrictions.eq("retired", Boolean.FALSE)); + cq.where(cb.isFalse(root.get("retired"))); } - return crit.list(); + + return session.createQuery(cq).getResultList(); } - + + /** * @see org.openmrs.api.db.OrderSetDAO#getOrderSetById(Integer) */ @Override public OrderSet getOrderSetById(Integer orderSetId) throws DAOException { - return (OrderSet) sessionFactory.getCurrentSession().get(OrderSet.class, orderSetId); + return sessionFactory.getCurrentSession().get(OrderSet.class, orderSetId); } /** @@ -89,27 +95,29 @@ public OrderSet getOrderSetById(Integer orderSetId) throws DAOException { */ @Override public OrderSet getOrderSetByUniqueUuid(String orderSetUuid) throws DAOException { - return (OrderSet) sessionFactory.getCurrentSession().createQuery("from OrderSet o where o.uuid = :uuid").setString( - "uuid", orderSetUuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderSet.class, orderSetUuid); } - /** * @see org.openmrs.api.db.OrderSetDAO#getOrderSetMemberByUuid(String) */ @Override public OrderSetMember getOrderSetMemberByUuid(String uuid) throws DAOException { - return (OrderSetMember) sessionFactory.getCurrentSession().createQuery("from OrderSetMember osm where osm.uuid = :uuid").setString( - "uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderSetMember.class, uuid); } /** * @see org.openmrs.api.db.OrderSetDAO#getAllOrderSetAttributeTypes() */ - @SuppressWarnings("unchecked") @Override public List getAllOrderSetAttributeTypes() { - return sessionFactory.getCurrentSession().createCriteria(OrderSetAttributeType.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderSetAttributeType.class); + cq.from(OrderSetAttributeType.class); + + return session.createQuery(cq).getResultList(); + } /** @@ -125,8 +133,7 @@ public OrderSetAttributeType getOrderSetAttributeType(Integer id) { */ @Override public OrderSetAttributeType getOrderSetAttributeTypeByUuid(String uuid) { - return (OrderSetAttributeType) sessionFactory.getCurrentSession().createCriteria(OrderSetAttributeType.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderSetAttributeType.class, uuid); } /** @@ -151,8 +158,7 @@ public void deleteOrderSetAttributeType(OrderSetAttributeType orderSetAttributeT */ @Override public OrderSetAttribute getOrderSetAttributeByUuid(String uuid) { - return (OrderSetAttribute) sessionFactory.getCurrentSession().createCriteria(OrderSetAttribute.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, OrderSetAttribute.class, uuid); } /** @@ -160,8 +166,13 @@ public OrderSetAttribute getOrderSetAttributeByUuid(String uuid) { */ @Override public OrderSetAttributeType getOrderSetAttributeTypeByName(String name) { - return (OrderSetAttributeType) sessionFactory.getCurrentSession().createCriteria(OrderSetAttributeType.class).add( - Restrictions.eq("name", name)).uniqueResult(); - } + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(OrderSetAttributeType.class); + Root root = cq.from(OrderSetAttributeType.class); + + cq.where(cb.equal(root.get("name"), name)); + return session.createQuery(cq).uniqueResult(); + } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernatePersonDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernatePersonDAO.java index 6a5839d21219..0bb9fedd3e87 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernatePersonDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernatePersonDAO.java @@ -9,6 +9,11 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashSet; @@ -16,13 +21,9 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; -import org.hibernate.Query; import org.hibernate.SQLQuery; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; -import org.hibernate.type.StringType; import org.openmrs.Person; import org.openmrs.PersonAddress; import org.openmrs.PersonAttribute; @@ -240,17 +241,23 @@ public List getPeople(String searchString, Boolean dead, Boolean voided) boolean includeVoided = (voided != null) ? voided : false; if (StringUtils.isBlank(searchString)) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Person.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Person.class); + Root root = cq.from(Person.class); + + List predicates = new ArrayList<>(); if (dead != null) { - criteria.add(Restrictions.eq("dead", dead)); + predicates.add(cb.equal(root.get("dead"), dead)); } if (!includeVoided) { - criteria.add(Restrictions.eq("personVoided", false)); + predicates.add(cb.isFalse(root.get("personVoided"))); } - criteria.setMaxResults(maxResults); - return criteria.list(); + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).setMaxResults(maxResults).getResultList(); } String query = LuceneQuery.escapeQuery(searchString); @@ -304,7 +311,7 @@ public static Integer getMaximumSearchResults() { */ @Override public Person getPerson(Integer personId) { - return (Person) sessionFactory.getCurrentSession().get(Person.class, personId); + return sessionFactory.getCurrentSession().get(Person.class, personId); } /** @@ -332,7 +339,7 @@ public PersonAttributeType savePersonAttributeType(PersonAttributeType type) { */ @Override public PersonAttributeType getPersonAttributeType(Integer typeId) { - return (PersonAttributeType) sessionFactory.getCurrentSession().get(PersonAttributeType.class, typeId); + return sessionFactory.getCurrentSession().get(PersonAttributeType.class, typeId); } /** @@ -341,7 +348,7 @@ public PersonAttributeType getPersonAttributeType(Integer typeId) { */ @Override public PersonAttribute getPersonAttribute(Integer id) { - return (PersonAttribute) sessionFactory.getCurrentSession().get(PersonAttribute.class, id); + return sessionFactory.getCurrentSession().get(PersonAttribute.class, id); } /** @@ -349,49 +356,55 @@ public PersonAttribute getPersonAttribute(Integer id) { * @see org.openmrs.api.db.PersonDAO#getAllPersonAttributeTypes(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllPersonAttributeTypes(boolean includeRetired) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PersonAttributeType.class, "r"); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(PersonAttributeType.class); + Root root = cq.from(PersonAttributeType.class); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - criteria.addOrder(Order.asc("sortWeight")); - - return criteria.list(); + + cq.orderBy(cb.asc(root.get("sortWeight"))); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.PersonDAO#getPersonAttributeTypes(java.lang.String, java.lang.String, * java.lang.Integer, java.lang.Boolean) */ @Override // TODO - PersonServiceTest fails here - @SuppressWarnings("unchecked") - public List getPersonAttributeTypes(String exactName, String format, Integer foreignKey, - Boolean searchable) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PersonAttributeType.class, "r"); - + public List getPersonAttributeTypes(String exactName, String format, Integer foreignKey, Boolean searchable) throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(PersonAttributeType.class); + Root root = cq.from(PersonAttributeType.class); + + List predicates = new ArrayList<>(); if (exactName != null) { - criteria.add(Restrictions.eq("name", exactName)); + predicates.add(cb.equal(root.get("name"), exactName)); } - + if (format != null) { - criteria.add(Restrictions.eq("format", format)); + predicates.add(cb.equal(root.get("format"), format)); } - + if (foreignKey != null) { - criteria.add(Restrictions.eq("foreignKey", foreignKey)); + predicates.add(cb.equal(root.get("foreignKey"), foreignKey)); } - + if (searchable != null) { - criteria.add(Restrictions.eq("searchable", searchable)); + predicates.add(cb.equal(root.get("searchable"), searchable)); } - - return criteria.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.PersonService#getRelationship(java.lang.Integer) * @see org.openmrs.api.db.PersonDAO#getRelationship(java.lang.Integer) @@ -399,7 +412,7 @@ public List getPersonAttributeTypes(String exactName, Strin @Override public Relationship getRelationship(Integer relationshipId) throws DAOException { - return (Relationship) sessionFactory.getCurrentSession() + return sessionFactory.getCurrentSession() .get(Relationship.class, relationshipId); } @@ -408,17 +421,20 @@ public Relationship getRelationship(Integer relationshipId) throws DAOException * @see org.openmrs.api.db.PersonDAO#getAllRelationships(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllRelationships(boolean includeVoided) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Relationship.class, "r"); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Relationship.class); + Root root = cq.from(Relationship.class); + if (!includeVoided) { - criteria.add(Restrictions.eq("voided", false)); + cq.where(cb.isFalse(root.get("voided"))); } - - return criteria.list(); + + return session.createQuery(cq).getResultList(); } - + + /** * @see org.openmrs.api.PersonService#getRelationships(org.openmrs.Person, org.openmrs.Person, * org.openmrs.RelationshipType) @@ -426,25 +442,30 @@ public List getAllRelationships(boolean includeVoided) throws DAOE * org.openmrs.RelationshipType) */ @Override - @SuppressWarnings("unchecked") public List getRelationships(Person fromPerson, Person toPerson, RelationshipType relType) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Relationship.class, "r"); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Relationship.class); + Root root = cq.from(Relationship.class); + + List predicates = new ArrayList<>(); if (fromPerson != null) { - criteria.add(Restrictions.eq("personA", fromPerson)); + predicates.add(cb.equal(root.get("personA"), fromPerson)); } if (toPerson != null) { - criteria.add(Restrictions.eq("personB", toPerson)); + predicates.add(cb.equal(root.get("personB"), toPerson)); } if (relType != null) { - criteria.add(Restrictions.eq("relationshipType", relType)); + predicates.add(cb.equal(root.get("relationshipType"), relType)); } - - criteria.add(Restrictions.eq("voided", false)); - - return criteria.list(); + + predicates.add(cb.isFalse(root.get("voided"))); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.PersonService#getRelationships(org.openmrs.Person, org.openmrs.Person, * org.openmrs.RelationshipType, java.util.Date, java.util.Date) @@ -452,49 +473,63 @@ public List getRelationships(Person fromPerson, Person toPerson, R * org.openmrs.RelationshipType, java.util.Date, java.util.Date) */ @Override - @SuppressWarnings("unchecked") - public List getRelationships(Person fromPerson, Person toPerson, RelationshipType relType, - Date startEffectiveDate, Date endEffectiveDate) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Relationship.class, "r"); - + public List getRelationships(Person fromPerson, Person toPerson, RelationshipType relType, Date startEffectiveDate, Date endEffectiveDate) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Relationship.class); + Root root = cq.from(Relationship.class); + + List predicates = new ArrayList<>(); if (fromPerson != null) { - criteria.add(Restrictions.eq("personA", fromPerson)); + predicates.add(cb.equal(root.get("personA"), fromPerson)); } if (toPerson != null) { - criteria.add(Restrictions.eq("personB", toPerson)); + predicates.add(cb.equal(root.get("personB"), toPerson)); } if (relType != null) { - criteria.add(Restrictions.eq("relationshipType", relType)); + predicates.add(cb.equal(root.get("relationshipType"), relType)); } if (startEffectiveDate != null) { - criteria.add(Restrictions.disjunction().add( - Restrictions.and(Restrictions.le("startDate", startEffectiveDate), Restrictions.ge("endDate", - startEffectiveDate))).add( - Restrictions.and(Restrictions.le("startDate", startEffectiveDate), Restrictions.isNull("endDate"))).add( - Restrictions.and(Restrictions.isNull("startDate"), Restrictions.ge("endDate", startEffectiveDate))).add( - Restrictions.and(Restrictions.isNull("startDate"), Restrictions.isNull("endDate")))); + Predicate startDatePredicate = cb.or( + cb.and(cb.lessThanOrEqualTo(root.get("startDate"), startEffectiveDate), + cb.greaterThanOrEqualTo(root.get("endDate"), startEffectiveDate)), + cb.and(cb.lessThanOrEqualTo(root.get("startDate"), startEffectiveDate), + cb.isNull(root.get("endDate"))), + cb.and(cb.isNull(root.get("startDate")), + cb.greaterThanOrEqualTo(root.get("endDate"), startEffectiveDate)), + cb.and(cb.isNull(root.get("startDate")), + cb.isNull(root.get("endDate"))) + ); + predicates.add(startDatePredicate); } if (endEffectiveDate != null) { - criteria.add(Restrictions.disjunction().add( - Restrictions.and(Restrictions.le("startDate", endEffectiveDate), Restrictions - .ge("endDate", endEffectiveDate))).add( - Restrictions.and(Restrictions.le("startDate", endEffectiveDate), Restrictions.isNull("endDate"))).add( - Restrictions.and(Restrictions.isNull("startDate"), Restrictions.ge("endDate", endEffectiveDate))).add( - Restrictions.and(Restrictions.isNull("startDate"), Restrictions.isNull("endDate")))); + Predicate endDatePredicate = cb.or( + cb.and(cb.lessThanOrEqualTo(root.get("startDate"), endEffectiveDate), + cb.greaterThanOrEqualTo(root.get("endDate"), endEffectiveDate)), + cb.and(cb.lessThanOrEqualTo(root.get("startDate"), endEffectiveDate), + cb.isNull(root.get("endDate"))), + cb.and(cb.isNull(root.get("startDate")), + cb.greaterThanOrEqualTo(root.get("endDate"), endEffectiveDate)), + cb.and(cb.isNull(root.get("startDate")), + cb.isNull(root.get("endDate"))) + ); + predicates.add(endDatePredicate); } - criteria.add(Restrictions.eq("voided", false)); - - return criteria.list(); + + predicates.add(cb.isFalse(root.get("voided"))); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.PersonService#getRelationshipType(java.lang.Integer) * @see org.openmrs.api.db.PersonDAO#getRelationshipType(java.lang.Integer) */ @Override public RelationshipType getRelationshipType(Integer relationshipTypeId) throws DAOException { - - return (RelationshipType) sessionFactory.getCurrentSession().get( + return sessionFactory.getCurrentSession().get( RelationshipType.class, relationshipTypeId); } @@ -503,20 +538,30 @@ public RelationshipType getRelationshipType(Integer relationshipTypeId) throws D * @see org.openmrs.api.db.PersonDAO#getRelationshipTypes(java.lang.String, java.lang.Boolean) */ @Override - @SuppressWarnings("unchecked") public List getRelationshipTypes(String relationshipTypeName, Boolean preferred) throws DAOException { - - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(RelationshipType.class); - criteria.add(Restrictions.sqlRestriction("CONCAT(a_Is_To_B, CONCAT('/', b_Is_To_A)) like (?)", relationshipTypeName, - new StringType())); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(RelationshipType.class); + Root root = cq.from(RelationshipType.class); + + List predicates = new ArrayList<>(); + if (StringUtils.isNotEmpty(relationshipTypeName)) { + Expression concatenatedFields = cb.concat(root.get("aIsToB"), + cb.concat("/", root.get("bIsToA"))); + predicates.add(cb.like(concatenatedFields, relationshipTypeName)); + } else { + // Add a predicate that is always false + predicates.add(cb.or()); + } + if (preferred != null) { - criteria.add(Restrictions.eq("preferred", preferred)); + predicates.add(cb.equal(root.get("preferred"), preferred)); } - - return criteria.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.PersonService#saveRelationshipType(org.openmrs.RelationshipType) * @see org.openmrs.api.db.PersonDAO#saveRelationshipType(org.openmrs.RelationshipType) @@ -619,8 +664,7 @@ public static void deletePersonAndAttributes(SessionFactory sessionFactory, Pers */ @Override public PersonAttributeType getPersonAttributeTypeByUuid(String uuid) { - return (PersonAttributeType) sessionFactory.getCurrentSession().createQuery( - "from PersonAttributeType pat where pat.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, PersonAttributeType.class, uuid); } /** @@ -630,7 +674,7 @@ public PersonAttributeType getPersonAttributeTypeByUuid(String uuid) { public String getSavedPersonAttributeTypeName(PersonAttributeType personAttributeType) { SQLQuery sql = sessionFactory.getCurrentSession().createSQLQuery( "select name from person_attribute_type where person_attribute_type_id = :personAttributeTypeId"); - sql.setInteger("personAttributeTypeId", personAttributeType.getId()); + sql.setParameter("personAttributeTypeId", personAttributeType.getId()); return (String) sql.uniqueResult(); } @@ -638,7 +682,7 @@ public String getSavedPersonAttributeTypeName(PersonAttributeType personAttribut public Boolean getSavedPersonAttributeTypeSearchable(PersonAttributeType personAttributeType) { SQLQuery sql = sessionFactory.getCurrentSession().createSQLQuery( "select searchable from person_attribute_type where person_attribute_type_id = :personAttributeTypeId"); - sql.setInteger("personAttributeTypeId", personAttributeType.getId()); + sql.setParameter("personAttributeTypeId", personAttributeType.getId()); return (Boolean) sql.uniqueResult(); } @@ -647,14 +691,12 @@ public Boolean getSavedPersonAttributeTypeSearchable(PersonAttributeType personA */ @Override public Person getPersonByUuid(String uuid) { - return (Person) sessionFactory.getCurrentSession().createQuery("from Person p where p.uuid = :uuid").setString( - "uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Person.class, uuid); } @Override public PersonAddress getPersonAddressByUuid(String uuid) { - return (PersonAddress) sessionFactory.getCurrentSession().createQuery("from PersonAddress p where p.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, PersonAddress.class, uuid); } /** @@ -671,7 +713,7 @@ public PersonMergeLog savePersonMergeLog(PersonMergeLog personMergeLog) throws D */ @Override public PersonMergeLog getPersonMergeLog(Integer id) throws DAOException { - return (PersonMergeLog) sessionFactory.getCurrentSession().get(PersonMergeLog.class, id); + return sessionFactory.getCurrentSession().get(PersonMergeLog.class, id); } /** @@ -679,8 +721,7 @@ public PersonMergeLog getPersonMergeLog(Integer id) throws DAOException { */ @Override public PersonMergeLog getPersonMergeLogByUuid(String uuid) throws DAOException { - return (PersonMergeLog) sessionFactory.getCurrentSession().createQuery("from PersonMergeLog p where p.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, PersonMergeLog.class, uuid); } /** @@ -690,7 +731,7 @@ public PersonMergeLog getPersonMergeLogByUuid(String uuid) throws DAOException { @SuppressWarnings("unchecked") public List getWinningPersonMergeLogs(Person person) throws DAOException { return (List) sessionFactory.getCurrentSession().createQuery( - "from PersonMergeLog p where p.winner.id = :winnerId").setInteger("winnerId", person.getId()).list(); + "from PersonMergeLog p where p.winner.id = :winnerId").setParameter("winnerId", person.getId()).list(); } /** @@ -699,7 +740,7 @@ public List getWinningPersonMergeLogs(Person person) throws DAOE @Override public PersonMergeLog getLosingPersonMergeLogs(Person person) throws DAOException { return (PersonMergeLog) sessionFactory.getCurrentSession().createQuery( - "from PersonMergeLog p where p.loser.id = :loserId").setInteger("loserId", person.getId()).uniqueResult(); + "from PersonMergeLog p where p.loser.id = :loserId").setParameter("loserId", person.getId()).uniqueResult(); } /** @@ -713,8 +754,7 @@ public List getAllPersonMergeLogs() throws DAOException { @Override public PersonAttribute getPersonAttributeByUuid(String uuid) { - return (PersonAttribute) sessionFactory.getCurrentSession().createQuery( - "from PersonAttribute p where p.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, PersonAttribute.class, uuid); } /** @@ -722,7 +762,7 @@ public PersonAttribute getPersonAttributeByUuid(String uuid) { */ @Override public PersonName getPersonName(Integer personNameId) { - return (PersonName) sessionFactory.getCurrentSession().get(PersonName.class, personNameId); + return sessionFactory.getCurrentSession().get(PersonName.class, personNameId); } /** @@ -730,8 +770,7 @@ public PersonName getPersonName(Integer personNameId) { */ @Override public PersonName getPersonNameByUuid(String uuid) { - return (PersonName) sessionFactory.getCurrentSession().createQuery("from PersonName p where p.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, PersonName.class, uuid); } /** @@ -739,8 +778,7 @@ public PersonName getPersonNameByUuid(String uuid) { */ @Override public Relationship getRelationshipByUuid(String uuid) { - return (Relationship) sessionFactory.getCurrentSession().createQuery("from Relationship r where r.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Relationship.class, uuid); } /** @@ -748,26 +786,28 @@ public Relationship getRelationshipByUuid(String uuid) { */ @Override public RelationshipType getRelationshipTypeByUuid(String uuid) { - return (RelationshipType) sessionFactory.getCurrentSession().createQuery( - "from RelationshipType rt where rt.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, RelationshipType.class, uuid); } /** * @see org.openmrs.api.db.PersonDAO#getAllRelationshipTypes(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllRelationshipTypes(boolean includeRetired) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(RelationshipType.class); - criteria.addOrder(Order.asc("weight")); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(RelationshipType.class); + Root root = cq.from(RelationshipType.class); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - - return criteria.list(); + + cq.orderBy(cb.asc(root.get("weight"))); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.PersonService#savePersonName(org.openmrs.PersonName) * @see org.openmrs.api.db.PersonDAO#savePersonName(org.openmrs.PersonName) diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateProgramWorkflowDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateProgramWorkflowDAO.java index 5a72fa2ca593..6da4177e94d7 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateProgramWorkflowDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateProgramWorkflowDAO.java @@ -9,6 +9,12 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.Query; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; @@ -16,12 +22,8 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; -import org.hibernate.Query; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; import org.hibernate.FlushMode; import org.hibernate.type.StandardBasicTypes; import org.openmrs.Cohort; @@ -82,49 +84,62 @@ public Program saveProgram(Program program) throws DAOException { */ @Override public Program getProgram(Integer programId) throws DAOException { - return (Program) sessionFactory.getCurrentSession().get(Program.class, programId); + return sessionFactory.getCurrentSession().get(Program.class, programId); } /** * @see org.openmrs.api.db.ProgramWorkflowDAO#getAllPrograms(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllPrograms(boolean includeRetired) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Program.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Program.class); + Root root = cq.from(Program.class); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } - return criteria.list(); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ProgramWorkflowDAO#getProgramsByName(String, boolean) */ @Override public List getProgramsByName(String programName, boolean includeRetired) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Program.class); - criteria.add(Restrictions.eq("name", programName)); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Program.class); + Root root = cq.from(Program.class); + + List predicates = new ArrayList<>(); + predicates.add(cb.equal(root.get("name"), programName)); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } - - @SuppressWarnings("unchecked") - List list = criteria.list(); - - return list; + + cq.where(cb.and(predicates.toArray(new Predicate[]{}))); + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ProgramWorkflowDAO#findPrograms(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public List findPrograms(String nameFragment) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Program.class, "program"); - criteria.add(Restrictions.ilike("name", nameFragment, MatchMode.ANYWHERE)); - criteria.addOrder(Order.asc("name")); - return criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Program.class); + Root root = cq.from(Program.class); + + Predicate nameLike = cb.like(cb.lower(root.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(nameFragment)); + + cq.where(nameLike).orderBy(cb.asc(root.get("name"))); + + return session.createQuery(cq).getResultList(); } /** @@ -146,7 +161,7 @@ public void deleteProgram(Program program) throws DAOException { public PatientProgram savePatientProgram(PatientProgram patientProgram) throws DAOException { CustomDatatypeUtil.saveAttributesIfNecessary(patientProgram); - if (patientProgram.getPatientProgramId() == null) { + if (patientProgram.getPatientProgramId() == null) { sessionFactory.getCurrentSession().save(patientProgram); } else { sessionFactory.getCurrentSession().merge(patientProgram); @@ -160,7 +175,7 @@ public PatientProgram savePatientProgram(PatientProgram patientProgram) throws D */ @Override public PatientProgram getPatientProgram(Integer patientProgramId) throws DAOException { - return (PatientProgram) sessionFactory.getCurrentSession().get(PatientProgram.class, patientProgramId); + return sessionFactory.getCurrentSession().get(PatientProgram.class, patientProgramId); } /** @@ -168,35 +183,44 @@ public PatientProgram getPatientProgram(Integer patientProgramId) throws DAOExce * Date, Date, boolean) */ @Override - @SuppressWarnings("unchecked") public List getPatientPrograms(Patient patient, Program program, Date minEnrollmentDate, - Date maxEnrollmentDate, Date minCompletionDate, Date maxCompletionDate, boolean includeVoided) - throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(PatientProgram.class); + Date maxEnrollmentDate, Date minCompletionDate, Date maxCompletionDate, boolean includeVoided) + throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(PatientProgram.class); + Root root = cq.from(PatientProgram.class); + + List predicates = new ArrayList<>(); if (patient != null) { - crit.add(Restrictions.eq("patient", patient)); + predicates.add(cb.equal(root.get("patient"), patient)); } if (program != null) { - crit.add(Restrictions.eq("program", program)); + predicates.add(cb.equal(root.get("program"), program)); } if (minEnrollmentDate != null) { - crit.add(Restrictions.ge("dateEnrolled", minEnrollmentDate)); + predicates.add(cb.greaterThanOrEqualTo(root.get("dateEnrolled"), minEnrollmentDate)); } if (maxEnrollmentDate != null) { - crit.add(Restrictions.le("dateEnrolled", maxEnrollmentDate)); + predicates.add(cb.lessThanOrEqualTo(root.get("dateEnrolled"), maxEnrollmentDate)); } if (minCompletionDate != null) { - crit.add(Restrictions.or(Restrictions.isNull("dateCompleted"), Restrictions.ge("dateCompleted", - minCompletionDate))); + predicates.add(cb.or( + cb.isNull(root.get("dateCompleted")), + cb.greaterThanOrEqualTo(root.get("dateCompleted"), minCompletionDate) + )); } if (maxCompletionDate != null) { - crit.add(Restrictions.le("dateCompleted", maxCompletionDate)); + predicates.add(cb.lessThanOrEqualTo(root.get("dateCompleted"), maxCompletionDate)); } if (!includeVoided) { - crit.add(Restrictions.eq("voided", false)); + predicates.add(cb.isFalse(root.get("voided"))); } - crit.addOrder(Order.asc("dateEnrolled")); - return crit.list(); + + cq.where(cb.and(predicates.toArray(new Predicate[]{}))) + .orderBy(cb.asc(root.get("dateEnrolled"))); + + return session.createQuery(cq).getResultList(); } /** @@ -222,12 +246,12 @@ public List getPatientPrograms(Cohort cohort, Collection getAllConceptStateConversions() throws DAOException { - return sessionFactory.getCurrentSession().createCriteria(ConceptStateConversion.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptStateConversion.class); + cq.from(ConceptStateConversion.class); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ProgramWorkflowDAO#getConceptStateConversion(java.lang.Integer) */ @Override public ConceptStateConversion getConceptStateConversion(Integer conceptStateConversionId) { - return (ConceptStateConversion) sessionFactory.getCurrentSession().get(ConceptStateConversion.class, + return sessionFactory.getCurrentSession().get(ConceptStateConversion.class, conceptStateConversionId); } @@ -283,16 +311,21 @@ public void deleteConceptStateConversion(ConceptStateConversion csc) { */ @Override public ConceptStateConversion getConceptStateConversion(ProgramWorkflow workflow, Concept trigger) { - ConceptStateConversion csc = null; - - if (workflow != null && trigger != null) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptStateConversion.class, "csc"); - criteria.add(Restrictions.eq("csc.programWorkflow", workflow)); - criteria.add(Restrictions.eq("csc.concept", trigger)); - csc = (ConceptStateConversion) criteria.uniqueResult(); + if (workflow == null || trigger == null) { + return null; } - - return csc; + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ConceptStateConversion.class); + Root root = cq.from(ConceptStateConversion.class); + + cq.where(cb.and( + cb.equal(root.get("programWorkflow"), workflow), + cb.equal(root.get("concept"), trigger) + )); + + return session.createQuery(cq).uniqueResult(); } /** @@ -300,8 +333,7 @@ public ConceptStateConversion getConceptStateConversion(ProgramWorkflow workflow */ @Override public ConceptStateConversion getConceptStateConversionByUuid(String uuid) { - return (ConceptStateConversion) sessionFactory.getCurrentSession().createQuery( - "from ConceptStateConversion csc where csc.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ConceptStateConversion.class, uuid); } /** @@ -309,8 +341,7 @@ public ConceptStateConversion getConceptStateConversionByUuid(String uuid) { */ @Override public PatientProgram getPatientProgramByUuid(String uuid) { - return (PatientProgram) sessionFactory.getCurrentSession().createQuery( - "from PatientProgram pp where pp.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, PatientProgram.class, uuid); } /** @@ -318,8 +349,7 @@ public PatientProgram getPatientProgramByUuid(String uuid) { */ @Override public Program getProgramByUuid(String uuid) { - return (Program) sessionFactory.getCurrentSession().createQuery("from Program p where p.uuid = :uuid").setString( - "uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Program.class, uuid); } /** @@ -327,7 +357,7 @@ public Program getProgramByUuid(String uuid) { */ @Override public ProgramWorkflowState getState(Integer stateId) { - return (ProgramWorkflowState) sessionFactory.getCurrentSession().get(ProgramWorkflowState.class, stateId); + return sessionFactory.getCurrentSession().get(ProgramWorkflowState.class, stateId); } /** @@ -335,14 +365,12 @@ public ProgramWorkflowState getState(Integer stateId) { */ @Override public ProgramWorkflowState getStateByUuid(String uuid) { - return (ProgramWorkflowState) sessionFactory.getCurrentSession().createQuery( - "from ProgramWorkflowState pws where pws.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ProgramWorkflowState.class, uuid); } @Override public PatientState getPatientStateByUuid(String uuid) { - return (PatientState) sessionFactory.getCurrentSession().createQuery("from PatientState pws where pws.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, PatientState.class, uuid); } /** @@ -350,7 +378,7 @@ public PatientState getPatientStateByUuid(String uuid) { */ @Override public ProgramWorkflow getWorkflow(Integer workflowId) { - return (ProgramWorkflow) sessionFactory.getCurrentSession().get(ProgramWorkflow.class, workflowId); + return sessionFactory.getCurrentSession().get(ProgramWorkflow.class, workflowId); } /** @@ -358,8 +386,7 @@ public ProgramWorkflow getWorkflow(Integer workflowId) { */ @Override public ProgramWorkflow getWorkflowByUuid(String uuid) { - return (ProgramWorkflow) sessionFactory.getCurrentSession().createQuery( - "from ProgramWorkflow pw where pw.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ProgramWorkflow.class, uuid); } /** @@ -369,8 +396,8 @@ public ProgramWorkflow getWorkflowByUuid(String uuid) { public List getProgramsByConcept(Concept concept) { String pq = "select distinct p from Program p where p.concept = :concept"; Query pquery = sessionFactory.getCurrentSession().createQuery(pq); - pquery.setEntity("concept", concept); - return pquery.list(); + pquery.setParameter("concept", concept); + return pquery.getResultList(); } /** @@ -380,8 +407,8 @@ public List getProgramsByConcept(Concept concept) { public List getProgramWorkflowsByConcept(Concept concept) { String wq = "select distinct w from ProgramWorkflow w where w.concept = :concept"; Query wquery = sessionFactory.getCurrentSession().createQuery(wq); - wquery.setEntity("concept", concept); - return wquery.list(); + wquery.setParameter("concept", concept); + return wquery.getResultList(); } /** @@ -391,89 +418,93 @@ public List getProgramWorkflowsByConcept(Concept concept) { public List getProgramWorkflowStatesByConcept(Concept concept) { String sq = "select distinct s from ProgramWorkflowState s where s.concept = :concept"; Query squery = sessionFactory.getCurrentSession().createQuery(sq); - squery.setEntity("concept", concept); - return squery.list(); + squery.setParameter("concept", concept); + return squery.getResultList(); } - @Override - public List getAllProgramAttributeTypes() { - return sessionFactory.getCurrentSession().createCriteria(ProgramAttributeType.class).list(); - } - - @Override - public ProgramAttributeType getProgramAttributeType(Integer id) { - return (ProgramAttributeType) sessionFactory.getCurrentSession().get(ProgramAttributeType.class, id); - } - - @Override - public ProgramAttributeType getProgramAttributeTypeByUuid(String uuid) { - return (ProgramAttributeType) sessionFactory.getCurrentSession().createCriteria(ProgramAttributeType.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); - } - - @Override - public ProgramAttributeType saveProgramAttributeType(ProgramAttributeType programAttributeType) { - sessionFactory.getCurrentSession().saveOrUpdate(programAttributeType); - return programAttributeType; - } - - @Override - public PatientProgramAttribute getPatientProgramAttributeByUuid(String uuid) { - return (PatientProgramAttribute) sessionFactory.getCurrentSession().createCriteria(PatientProgramAttribute.class).add(Restrictions.eq("uuid", uuid)).uniqueResult(); - } - - @Override - public void purgeProgramAttributeType(ProgramAttributeType type) { - sessionFactory.getCurrentSession().delete(type); - } - - @Override - public List getPatientProgramByAttributeNameAndValue(String attributeName, String attributeValue) { - FlushMode flushMode = sessionFactory.getCurrentSession().getHibernateFlushMode(); - sessionFactory.getCurrentSession().setHibernateFlushMode(FlushMode.MANUAL); - Query query; - try { - query = sessionFactory.getCurrentSession().createQuery( - "SELECT pp FROM patient_program pp " + - "INNER JOIN pp.attributes attr " + - "INNER JOIN attr.attributeType attr_type " + - "WHERE attr.valueReference = :attributeValue " + - "AND attr_type.name = :attributeName " + - "AND pp.voided = 0") - .setParameter("attributeName", attributeName) - .setParameter("attributeValue", attributeValue); - return query.list(); - } finally { - sessionFactory.getCurrentSession().setHibernateFlushMode(flushMode); - } - } - - @Override - public Map getPatientProgramAttributeByAttributeName(List patientIds, String attributeName) { - Map patientProgramAttributes = new HashMap<>(); - if (patientIds.isEmpty() || attributeName == null) { - return patientProgramAttributes; - } - String commaSeperatedPatientIds = StringUtils.join(patientIds, ","); - List list = sessionFactory.getCurrentSession().createSQLQuery( - "SELECT p.patient_id as person_id, " + - " concat('{',group_concat(DISTINCT (coalesce(concat('\"',ppt.name,'\":\"', COALESCE (cn.name, ppa.value_reference),'\"'))) SEPARATOR ','),'}') AS patientProgramAttributeValue " + - " from patient p " + - " join patient_program pp on p.patient_id = pp.patient_id and p.patient_id in (" + commaSeperatedPatientIds + ")" + - " join patient_program_attribute ppa on pp.patient_program_id = ppa.patient_program_id and ppa.voided=0" + - " join program_attribute_type ppt on ppa.attribute_type_id = ppt.program_attribute_type_id and ppt.name ='" + attributeName + "' "+ - " LEFT OUTER JOIN concept_name cn on ppa.value_reference = cn.concept_id and cn.concept_name_type= 'FULLY_SPECIFIED' and cn.voided=0 and ppt.datatype like '%ConceptDataType%'" + - " group by p.patient_id") - .addScalar("person_id", StandardBasicTypes.INTEGER) - .addScalar("patientProgramAttributeValue", StandardBasicTypes.STRING) - .list(); - - for (Object o : list) { - Object[] arr = (Object[]) o; - patientProgramAttributes.put(arr[0], arr[1]); - } - - return patientProgramAttributes; - - } + @Override + public List getAllProgramAttributeTypes() { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ProgramAttributeType.class); + cq.from(ProgramAttributeType.class); + + return session.createQuery(cq).getResultList(); + } + + @Override + public ProgramAttributeType getProgramAttributeType(Integer id) { + return sessionFactory.getCurrentSession().get(ProgramAttributeType.class, id); + } + + @Override + public ProgramAttributeType getProgramAttributeTypeByUuid(String uuid) { + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, ProgramAttributeType.class, uuid); + } + + @Override + public ProgramAttributeType saveProgramAttributeType(ProgramAttributeType programAttributeType) { + sessionFactory.getCurrentSession().saveOrUpdate(programAttributeType); + return programAttributeType; + } + + @Override + public PatientProgramAttribute getPatientProgramAttributeByUuid(String uuid) { + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, PatientProgramAttribute.class, uuid); + } + + @Override + public void purgeProgramAttributeType(ProgramAttributeType type) { + sessionFactory.getCurrentSession().delete(type); + } + + @Override + public List getPatientProgramByAttributeNameAndValue(String attributeName, String attributeValue) { + FlushMode flushMode = sessionFactory.getCurrentSession().getHibernateFlushMode(); + sessionFactory.getCurrentSession().setHibernateFlushMode(FlushMode.MANUAL); + Query query; + try { + query = sessionFactory.getCurrentSession().createQuery( + "SELECT pp FROM patient_program pp " + + "INNER JOIN pp.attributes attr " + + "INNER JOIN attr.attributeType attr_type " + + "WHERE attr.valueReference = :attributeValue " + + "AND attr_type.name = :attributeName " + + "AND pp.voided = 0") + .setParameter("attributeName", attributeName) + .setParameter("attributeValue", attributeValue); + return query.getResultList(); + } finally { + sessionFactory.getCurrentSession().setHibernateFlushMode(flushMode); + } + } + + @Override + public Map getPatientProgramAttributeByAttributeName(List patientIds, String attributeName) { + Map patientProgramAttributes = new HashMap<>(); + if (patientIds.isEmpty() || attributeName == null) { + return patientProgramAttributes; + } + String commaSeperatedPatientIds = StringUtils.join(patientIds, ","); + List list = sessionFactory.getCurrentSession().createSQLQuery( + "SELECT p.patient_id as person_id, " + + " concat('{',group_concat(DISTINCT (coalesce(concat('\"',ppt.name,'\":\"', COALESCE (cn.name, ppa.value_reference),'\"'))) SEPARATOR ','),'}') AS patientProgramAttributeValue " + + " from patient p " + + " join patient_program pp on p.patient_id = pp.patient_id and p.patient_id in (" + commaSeperatedPatientIds + ")" + + " join patient_program_attribute ppa on pp.patient_program_id = ppa.patient_program_id and ppa.voided=0" + + " join program_attribute_type ppt on ppa.attribute_type_id = ppt.program_attribute_type_id and ppt.name ='" + attributeName + "' "+ + " LEFT OUTER JOIN concept_name cn on ppa.value_reference = cn.concept_id and cn.concept_name_type= 'FULLY_SPECIFIED' and cn.voided=0 and ppt.datatype like '%ConceptDataType%'" + + " group by p.patient_id") + .addScalar("person_id", StandardBasicTypes.INTEGER) + .addScalar("patientProgramAttributeValue", StandardBasicTypes.STRING) + .list(); + + for (Object o : list) { + Object[] arr = (Object[]) o; + patientProgramAttributes.put(arr[0], arr[1]); + } + + return patientProgramAttributes; + + } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateProviderDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateProviderDAO.java index d8af5e58010b..a42717c3fb89 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateProviderDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateProviderDAO.java @@ -9,29 +9,30 @@ */ package org.openmrs.api.db.hibernate; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Conjunction; -import org.hibernate.criterion.Disjunction; -import org.hibernate.criterion.Junction; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Restrictions; -import org.hibernate.sql.JoinType; import org.openmrs.Person; +import org.openmrs.PersonName; import org.openmrs.Provider; import org.openmrs.ProviderAttribute; import org.openmrs.ProviderAttributeType; import org.openmrs.api.context.Context; -import org.openmrs.api.db.DAOException; import org.openmrs.api.db.ProviderDAO; import org.openmrs.util.OpenmrsConstants; @@ -83,7 +84,7 @@ public void deleteProvider(Provider provider) { */ @Override public Provider getProvider(Integer id) { - return (Provider) getSession().get(Provider.class, id); + return getSession().get(Provider.class, id); } /** @@ -99,26 +100,34 @@ public Provider getProviderByUuid(String uuid) { */ @Override public Collection getProvidersByPerson(Person person, boolean includeRetired) { - Criteria criteria = getSession().createCriteria(Provider.class); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Provider.class); + Root root = cq.from(Provider.class); + + List predicates = new ArrayList<>(); + List orders = new ArrayList<>(); + if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } else { //push retired Provider to the end of the returned list - criteria.addOrder(Order.asc("retired")); + orders.add(cb.asc(root.get("retired"))); } - criteria.add(Restrictions.eq("person", person)); + predicates.add(cb.equal(root.get("person"), person)); - criteria.addOrder(Order.asc("providerId")); + orders.add(cb.asc(root.get("providerId"))); - return criteria.list(); + cq.where(predicates.toArray(new Predicate[]{})).orderBy(orders); + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.api.db.ProviderDAO#getProviderAttribute(Integer) */ @Override public ProviderAttribute getProviderAttribute(Integer providerAttributeID) { - return (ProviderAttribute) getSession().get(ProviderAttribute.class, providerAttributeID); + return getSession().get(ProviderAttribute.class, providerAttributeID); } /** @@ -136,20 +145,28 @@ public ProviderAttribute getProviderAttributeByUuid(String uuid) { @Override public List getProviders(String name, Map serializedAttributeValues, Integer start, Integer length, boolean includeRetired) { - Criteria criteria = prepareProviderCriteria(name, includeRetired); - if (start != null) { - criteria.setFirstResult(start); - } - if (length != null) { - criteria.setMaxResults(length); - } + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Provider.class); + Root root = cq.from(Provider.class); + + List predicates = prepareProviderCriteria(cb, root, name, includeRetired); + cq.where(predicates.toArray(new Predicate[]{})).distinct(true); if (includeRetired) { //push retired Provider to the end of the returned list - criteria.addOrder(Order.asc("retired")); + cq.orderBy(cb.asc(root.get("retired"))); + } + + TypedQuery typedQuery = session.createQuery(cq); + if (start != null) { + typedQuery.setFirstResult(start); + } + if (length != null) { + typedQuery.setMaxResults(length); } - List providers = criteria.list(); + List providers = typedQuery.getResultList(); if (serializedAttributeValues != null) { CollectionUtils.filter(providers, new AttributeMatcherPredicate( serializedAttributeValues)); @@ -172,79 +189,89 @@ private MatchMode getMatchMode() { } return MatchMode.EXACT; } - + /** - * Creates a Provider Criteria based on name + * Prepares a list of JPA predicates for searching Provider entities based on a specified name + * and retirement status. * - * @param name represents provider name - * @param includeRetired - * @return Criteria represents the hibernate criteria to search + * @param cb The CriteriaBuilder used for creating predicates. + * @param root The root entity (Provider) in the CriteriaQuery. + * @param name The provider's name or a part of it to be used in the search. If blank, it defaults to a wildcard search. + * @param includeRetired Boolean flag indicating whether to include retired providers in the search. + * @return List A list of predicates that can be added to a CriteriaQuery for filtering Provider entities. */ - private Criteria prepareProviderCriteria(String name, boolean includeRetired) { + private List prepareProviderCriteria(CriteriaBuilder cb, Root root, String name, boolean includeRetired) { if (StringUtils.isBlank(name)) { name = "%"; } - - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Provider.class).createAlias("person", "p", - JoinType.LEFT_OUTER_JOIN); - + + List predicates = new ArrayList<>(); if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } - - criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); - - criteria.createAlias("p.names", "personName", JoinType.LEFT_OUTER_JOIN); - - Disjunction or = Restrictions.disjunction(); - or.add(Restrictions.ilike("identifier", name, getMatchMode())); - or.add(Restrictions.ilike("name", name, MatchMode.ANYWHERE)); - - Conjunction and = Restrictions.conjunction(); - or.add(and); - + + Predicate orCondition = cb.or( + cb.like(cb.lower(root.get("identifier")), getMatchMode().toLowerCasePattern(name)), + cb.like(cb.lower(root.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(name)) + ); + + Join personJoin = root.join("person", JoinType.LEFT); + Join personNameJoin = personJoin.join("names", JoinType.LEFT); + + List splitNamePredicates = new ArrayList<>(); String[] splitNames = name.split(" "); + for (String splitName : splitNames) { - and.add(getNameSearchExpression(splitName)); + splitNamePredicates.add(getNameSearchExpression(splitName, cb, personNameJoin)); } + Predicate andCondition = cb.and(splitNamePredicates.toArray(new Predicate[]{})); + + predicates.add(cb.or(orCondition, andCondition)); - criteria.add(or); - - return criteria; + return predicates; } /** * Creates or that matches the input name with Provider-Person-Names (not voided) * - * @param name - * @return Junction + * @param name The name string to be matched against the PersonName fields. + * @param cb The CriteriaBuilder used for creating the CriteriaQuery predicates. + * @param personNameJoin The join to the PersonName entity, allowing access to its fields. + * @return Predicate The compound predicate representing the desired search conditions. */ - private Junction getNameSearchExpression(String name) { + private Predicate getNameSearchExpression(String name, CriteriaBuilder cb, Join personNameJoin) { MatchMode mode = MatchMode.ANYWHERE; - - Conjunction and = Restrictions.conjunction(); - and.add(Restrictions.eq("personName.voided", false)); - - Disjunction or = Restrictions.disjunction(); - and.add(or); - - or.add(Restrictions.ilike("personName.givenName", name, mode)); - or.add(Restrictions.ilike("personName.middleName", name, mode)); - or.add(Restrictions.ilike("personName.familyName", name, mode)); - or.add(Restrictions.ilike("personName.familyName2", name, mode)); - - return and; + + Predicate voidedPredicate = cb.isFalse(personNameJoin.get("voided")); + + Predicate givenNamePredicate = cb.like(cb.lower(personNameJoin.get("givenName")), mode.toLowerCasePattern(name)); + Predicate middleNamePredicate = cb.like(cb.lower(personNameJoin.get("middleName")), mode.toLowerCasePattern(name)); + Predicate familyNamePredicate = cb.like(cb.lower(personNameJoin.get("familyName")), mode.toLowerCasePattern(name)); + Predicate familyName2Predicate = cb.like(cb.lower(personNameJoin.get("familyName2")), mode.toLowerCasePattern(name)); + + Predicate orPredicate = cb.or(givenNamePredicate, middleNamePredicate, familyNamePredicate, familyName2Predicate); + + return cb.and(voidedPredicate, orPredicate); } - + /** * @see org.openmrs.api.db.ProviderDAO#getCountOfProviders(String, boolean) */ @Override public Long getCountOfProviders(String name, boolean includeRetired) { - Criteria criteria = prepareProviderCriteria(name, includeRetired); - return (long) criteria.list().size(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(Provider.class); + + List predicates = prepareProviderCriteria(cb, root, name, includeRetired); + + cq.select(cb.countDistinct(root)).where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getSingleResult(); } - + + /* (non-Javadoc) * @see org.openmrs.api.db.ProviderDAO#getAllProviderAttributeTypes(boolean) */ @@ -252,23 +279,28 @@ public Long getCountOfProviders(String name, boolean includeRetired) { public List getAllProviderAttributeTypes(boolean includeRetired) { return getAll(includeRetired, ProviderAttributeType.class); } - + private List getAll(boolean includeRetired, Class clazz) { - Criteria criteria = getSession().createCriteria(clazz); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(clazz); + Root root = cq.from(clazz); + + List orderList = new ArrayList<>(); if (!includeRetired) { - criteria.add(Restrictions.eq("retired", false)); + cq.where(cb.isFalse(root.get("retired"))); } else { //push retired Provider to the end of the returned list - criteria.addOrder(Order.asc("retired")); + orderList.add(cb.asc(root.get("retired"))); } - criteria.addOrder(Order.asc("name")); - return criteria.list(); + orderList.add(cb.asc(root.get("name"))); + cq.orderBy(orderList); + + return session.createQuery(cq).getResultList(); } private T getByUuid(String uuid, Class clazz) { - Criteria criteria = getSession().createCriteria(clazz); - criteria.add(Restrictions.eq("uuid", uuid)); - return (T) criteria.uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, clazz, uuid); } /* (non-Javadoc) @@ -276,7 +308,7 @@ private T getByUuid(String uuid, Class clazz) { */ @Override public ProviderAttributeType getProviderAttributeType(Integer providerAttributeTypeId) { - return (ProviderAttributeType) getSession().get(ProviderAttributeType.class, providerAttributeTypeId); + return getSession().get(ProviderAttributeType.class, providerAttributeTypeId); } /* (non-Javadoc) @@ -287,6 +319,27 @@ public ProviderAttributeType getProviderAttributeTypeByUuid(String uuid) { return getByUuid(uuid, ProviderAttributeType.class); } + /* (non-Javadoc) + * @see org.openmrs.api.db.ProviderDAO#getProviderAttributeTypeByName(java.lang.String) + */ + @Override + public ProviderAttributeType getProviderAttributeTypeByName(String name) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ProviderAttributeType.class); + Root root = cq.from(ProviderAttributeType.class); + + cq.where(cb.isFalse(root.get("retired")), + cb.equal(root.get("name"), name)); + + List list = session.createQuery(cq).getResultList(); + + if (list.isEmpty()) { + return null; + } + return list.get(0); + } + /* (non-Javadoc) * @see org.openmrs.api.db.ProviderDAO#saveProviderAttributeType(org.openmrs.ProviderAttributeType) */ @@ -308,16 +361,22 @@ public void deleteProviderAttributeType(ProviderAttributeType providerAttributeT * @see org.openmrs.api.db.ProviderDAO#getProviderByIdentifier(java.lang.String) */ @Override - public boolean isProviderIdentifierUnique(Provider provider) throws DAOException { - - Criteria criteria = getSession().createCriteria(Provider.class); - criteria.add(Restrictions.eq("identifier", provider.getIdentifier())); + public boolean isProviderIdentifierUnique(Provider provider) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(Provider.class); + + List predicates = new ArrayList<>(); + predicates.add(cb.equal(root.get("identifier"), provider.getIdentifier())); if (provider.getProviderId() != null) { - criteria.add(Restrictions.not(Restrictions.eq("providerId", provider.getProviderId()))); + predicates.add(cb.notEqual(root.get("providerId"), provider.getProviderId())); } - criteria.setProjection(Projections.countDistinct("providerId")); - - return (Long) criteria.uniqueResult() == 0L; + + cq.select(cb.countDistinct(root.get("providerId"))) + .where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).uniqueResult() == 0L; } /** @@ -325,8 +384,13 @@ public boolean isProviderIdentifierUnique(Provider provider) throws DAOException */ @Override public Provider getProviderByIdentifier(String identifier) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Provider.class); - criteria.add(Restrictions.ilike("identifier", identifier, MatchMode.EXACT)); - return (Provider) criteria.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Provider.class); + Root root = cq.from(Provider.class); + + cq.where(cb.equal(cb.lower(root.get("identifier")), MatchMode.EXACT.toLowerCasePattern(identifier))); + + return session.createQuery(cq).uniqueResult(); } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateSerializedObjectDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateSerializedObjectDAO.java index 26cafbec9baa..6fdadb5558e9 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateSerializedObjectDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateSerializedObjectDAO.java @@ -9,14 +9,16 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.Date; import java.util.List; -import org.hibernate.Criteria; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Restrictions; import org.openmrs.Auditable; import org.openmrs.OpenmrsData; import org.openmrs.OpenmrsMetadata; @@ -66,7 +68,7 @@ public static HibernateSerializedObjectDAO getInstance() { @Override public SerializedObject getSerializedObject(Integer id) throws DAOException { if (id != null) { - return (SerializedObject) sessionFactory.getCurrentSession().get(SerializedObject.class, id); + return sessionFactory.getCurrentSession().get(SerializedObject.class, id); } return null; } @@ -85,13 +87,10 @@ public T getObject(Class baseClass, Integer id) thr */ @Override public SerializedObject getSerializedObjectByUuid(String uuid) throws DAOException { - SerializedObject ret = null; if (uuid != null) { - Criteria c = sessionFactory.getCurrentSession().createCriteria(SerializedObject.class); - c.add(Restrictions.eq("uuid", uuid)); - ret = (SerializedObject) c.uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, SerializedObject.class, uuid); } - return ret; + return null; } /** @@ -110,17 +109,28 @@ public T getObjectByUuid(Class baseClass, String uu * @see SerializedObjectDAO#getAllSerializedObjectsByName(Class, String, boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllSerializedObjectsByName(Class type, String name, boolean exactMatchOnly) - throws DAOException { - Criteria c = sessionFactory.getCurrentSession().createCriteria(SerializedObject.class); - c.add(Restrictions.or(Restrictions.eq("type", type.getName()), Restrictions.eq("subtype", type.getName()))); + throws DAOException { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(SerializedObject.class); + Root root = cq.from(SerializedObject.class); + + Predicate predicateForType = cb.or( + cb.equal(root.get("type"), type.getName()), + cb.equal(root.get("subtype"), type.getName()) + ); + + Predicate predicateForName; if (exactMatchOnly) { - c.add(Restrictions.eq("name", name)); + predicateForName = cb.equal(root.get("name"), name); } else { - c.add(Restrictions.ilike("name", name, MatchMode.ANYWHERE)); + predicateForName = cb.like(cb.lower(root.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(name)); } - return (List) c.list(); + + cq.where(predicateForType, predicateForName); + return session.createQuery(cq).getResultList(); } /** @@ -141,14 +151,25 @@ public List getAllObjectsByName(Class type, St * @see SerializedObjectDAO#getAllObjects(Class, boolean) */ @Override - @SuppressWarnings("unchecked") - public List getAllSerializedObjects(Class type, boolean includeRetired) throws DAOException { - Criteria c = sessionFactory.getCurrentSession().createCriteria(SerializedObject.class); - c.add(Restrictions.or(Restrictions.eq("type", type.getName()), Restrictions.eq("subtype", type.getName()))); + public List getAllSerializedObjects(Class type, boolean includeRetired) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(SerializedObject.class); + Root root = cq.from(SerializedObject.class); + + List predicates = new ArrayList<>(); + predicates.add(cb.or( + cb.equal(root.get("type"), type.getName()), + cb.equal(root.get("subtype"), type.getName()) + )); + if (!includeRetired) { - c.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } - return (List) c.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getResultList(); } /** diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateUserDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateUserDAO.java index 01abf0d42bdc..dccd01526616 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateUserDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateUserDAO.java @@ -9,6 +9,12 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.Query; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -18,13 +24,10 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; -import org.hibernate.Query; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; import org.openmrs.Person; +import org.openmrs.PersonName; import org.openmrs.Privilege; import org.openmrs.Role; import org.openmrs.User; @@ -66,7 +69,7 @@ public void setSessionFactory(SessionFactory sessionFactory) { } /** - * @see org.openmrs.api.UserService#saveUser(org.openmrs.User, java.lang.String) + * @see org.openmrs.api.UserService#saveUser(org.openmrs.User, java.lang.String, java.lang.String) */ @Override public User saveUser(User user, String password) { @@ -104,9 +107,9 @@ sequences and issues insert on session flush ( batching is possible) . public User getUserByUsername(String username) { Query query = sessionFactory.getCurrentSession().createQuery( "from User u where u.retired = '0' and (u.username = ?0 or u.systemId = ?1)"); - query.setString(0, username); - query.setString(1, username); - List users = query.list(); + query.setParameter(0, username); + query.setParameter(1, username); + List users = query.getResultList(); if (users == null || users.isEmpty()) { log.warn("request for username '" + username + "' not found"); @@ -120,9 +123,15 @@ public User getUserByUsername(String username) { * @see org.openmrs.api.UserService#getUserByEmail(java.lang.String) */ @Override - @SuppressWarnings("unchecked") public User getUserByEmail(String email) { - return (User) sessionFactory.getCurrentSession().createCriteria(User.class).add(Restrictions.eq("email", email).ignoreCase()).uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(User.class); + Root root = cq.from(User.class); + + cq.where(cb.equal(cb.lower(root.get("email")), email.toLowerCase())); + + return session.createQuery(cq).uniqueResult(); } /** @@ -131,16 +140,23 @@ public User getUserByEmail(String email) { @Override public LoginCredential getLoginCredentialByActivationKey(String activationKey) { String key = Security.encodeString(activationKey); - LoginCredential loginCred = (LoginCredential) sessionFactory.getCurrentSession().createCriteria(LoginCredential.class) - .add(Restrictions.like("activationKey", key, MatchMode.START)).uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(LoginCredential.class); + Root root = cq.from(LoginCredential.class); + + cq.where(cb.like(cb.lower(root.get("activationKey")), MatchMode.START.toCaseSensitivePattern(key))); + + LoginCredential loginCred = session.createQuery(cq).uniqueResult(); + if(loginCred != null) { String[] credTokens = loginCred.getActivationKey().split(":"); if(credTokens[0].equals(key)){ return loginCred; } - } + } return null; - } + } /** * @see org.openmrs.api.UserService#hasDuplicateUsername(org.openmrs.User) @@ -169,14 +185,14 @@ public boolean hasDuplicateUsername(String username, String systemId, Integer us .getCurrentSession() .createQuery( "select count(*) from User u where (u.username = :uname1 or u.systemId = :uname2 or u.username = :sysid1 or u.systemId = :sysid2 or u.systemId = :uname3) and u.userId <> :uid"); - query.setString("uname1", username); - query.setString("uname2", username); - query.setString("sysid1", systemId); - query.setString("sysid2", systemId); - query.setString("uname3", usernameWithCheckDigit); - query.setInteger("uid", userId); + query.setParameter("uname1", username); + query.setParameter("uname2", username); + query.setParameter("sysid1", systemId); + query.setParameter("sysid2", systemId); + query.setParameter("uname3", usernameWithCheckDigit); + query.setParameter("uid", userId); - Long count = (Long) query.uniqueResult(); + Long count = JpaUtils.getSingleResultOrNull(query); log.debug("# users found: " + count); return (count != null && count != 0); @@ -188,7 +204,7 @@ public boolean hasDuplicateUsername(String username, String systemId, Integer us @Override public User getUser(Integer userId) { - return (User) sessionFactory.getCurrentSession().get(User.class, userId); + return sessionFactory.getCurrentSession().get(User.class, userId); } /** @@ -198,8 +214,7 @@ public User getUser(Integer userId) { @SuppressWarnings("unchecked") public List getAllUsers() throws DAOException { return sessionFactory.getCurrentSession().createQuery("from User where not uuid = :daemonUserUuid order by userId") - .setString("daemonUserUuid", Daemon.getDaemonUserUuid()).list(); - + .setParameter("daemonUserUuid", Daemon.getDaemonUserUuid()).list(); } /** @@ -213,12 +228,19 @@ public void deleteUser(User user) { /** * @see org.openmrs.api.UserService#getUsersByRole(org.openmrs.Role) */ - @SuppressWarnings("unchecked") - public List getUsersByRole(Role role) throws DAOException { + public List getUsersByRole(Role role) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(User.class); + Root root = cq.from(User.class); + Join roles = root.join("roles"); - return (List) sessionFactory.getCurrentSession().createCriteria(User.class, "u").createCriteria("roles", "r") - .add(Restrictions.like("r.role", role.getRole())).add(Restrictions.ne("u.uuid", Daemon.getDaemonUserUuid())).addOrder(Order.asc("u.username")).list(); - + Predicate roleLike = cb.like(roles.get("role"), role.getRole()); + Predicate uuidNotEqual = cb.notEqual(root.get("uuid"), Daemon.getDaemonUserUuid()); + + cq.where(roleLike, uuidNotEqual).orderBy(cb.asc(root.get("username"))); + + return session.createQuery(cq).getResultList(); } /** @@ -235,7 +257,7 @@ public List getAllPrivileges() throws DAOException { */ @Override public Privilege getPrivilege(String p) throws DAOException { - return (Privilege) sessionFactory.getCurrentSession().get(Privilege.class, p); + return sessionFactory.getCurrentSession().get(Privilege.class, p); } /** @@ -286,7 +308,7 @@ public List getAllRoles() throws DAOException { */ @Override public Role getRole(String r) throws DAOException { - return (Role) sessionFactory.getCurrentSession().get(Role.class, r); + return sessionFactory.getCurrentSession().get(Role.class, r); } /** @@ -354,10 +376,10 @@ private void updateUserPassword(String newHashedPassword, String salt, Integer c * @see org.openmrs.api.UserService#changePassword(java.lang.String, java.lang.String) */ @Override - public void changePassword(String pw, String pw2) throws DAOException { + public void changePassword(String oldPassword, String newPassword) throws DAOException { User u = Context.getAuthenticatedUser(); LoginCredential credentials = getLoginCredential(u); - if (!credentials.checkPassword(pw)) { + if (!credentials.checkPassword(oldPassword)) { log.error("Passwords don't match"); throw new DAOException("Passwords don't match"); } @@ -366,7 +388,7 @@ public void changePassword(String pw, String pw2) throws DAOException { // update the user with the new password String salt = credentials.getSalt(); - String newHashedPassword = Security.encodeString(pw2 + salt); + String newHashedPassword = Security.encodeString(newPassword + salt); updateUserPassword(newHashedPassword, salt, u.getUserId(), new Date(), u.getUserId()); } @@ -437,7 +459,7 @@ public List getUsers(String name, List roles, boolean includeRetired query.setMaxResults(length); } - List returnList = query.list(); + List returnList = query.getResultList(); if (!CollectionUtils.isEmpty(returnList)) { returnList.sort(new UserByNameComparator()); @@ -456,11 +478,11 @@ public Integer generateSystemId() { Query query = sessionFactory.getCurrentSession().createQuery(hql); - Object object = query.uniqueResult(); + Object object = JpaUtils.getSingleResultOrNull(query); Integer id; if (object instanceof Number) { - id = ((Number) query.uniqueResult()).intValue() + 1; + id = ((Number) JpaUtils.getSingleResultOrNull(query)).intValue() + 1; } else { log.warn("What is being returned here? Definitely nothing expected object value: '" + object + "' of class: " + object.getClass()); @@ -475,17 +497,27 @@ public Integer generateSystemId() { */ @Override public List getUsersByName(String givenName, String familyName, boolean includeRetired) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(User.class); - crit.createAlias("person", "person"); - crit.createAlias("person.names", "names"); - crit.add(Restrictions.eq("names.givenName", givenName)); - crit.add(Restrictions.eq("names.familyName", familyName)); - crit.add(Restrictions.ne("uuid", Daemon.getDaemonUserUuid())); - crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(User.class); + Root root = cq.from(User.class); + + Join personJoin = root.join("person"); + Join nameJoin = personJoin.join("names"); + + List predicates = new ArrayList<>(); + predicates.add(cb.equal(nameJoin.get("givenName"), givenName)); + predicates.add(cb.equal(nameJoin.get("familyName"), familyName)); + predicates.add(cb.notEqual(root.get("uuid"), Daemon.getDaemonUserUuid())); + + if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } - return new ArrayList<>((List) crit.list()); + + cq.where(predicates.toArray(predicates.toArray(new Predicate[]{}))).distinct(true); + + return new ArrayList<>(session.createQuery(cq).getResultList()); } /** @@ -493,8 +525,7 @@ public List getUsersByName(String givenName, String familyName, boolean in */ @Override public Privilege getPrivilegeByUuid(String uuid) { - return (Privilege) sessionFactory.getCurrentSession().createQuery("from Privilege p where p.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Privilege.class, uuid); } /** @@ -502,8 +533,7 @@ public Privilege getPrivilegeByUuid(String uuid) { */ @Override public Role getRoleByUuid(String uuid) { - return (Role) sessionFactory.getCurrentSession().createQuery("from Role r where r.uuid = :uuid").setString("uuid", - uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Role.class, uuid); } /** @@ -515,8 +545,7 @@ public User getUserByUuid(String uuid) { if (uuid != null) { uuid = uuid.trim(); - ret = (User) sessionFactory.getCurrentSession().createQuery("from User u where u.uuid = :uuid").setString( - "uuid", uuid).uniqueResult(); + ret = HibernateUtil.getUniqueEntityByUUID(sessionFactory, User.class, uuid); } return ret; @@ -527,7 +556,7 @@ public User getUserByUuid(String uuid) { */ @Override public LoginCredential getLoginCredential(User user) { - return (LoginCredential) sessionFactory.getCurrentSession().get(LoginCredential.class, user.getUserId()); + return sessionFactory.getCurrentSession().get(LoginCredential.class, user.getUserId()); } /** @@ -538,8 +567,7 @@ public LoginCredential getLoginCredentialByUuid(String uuid) { if (uuid == null) { return null; } else { - return (LoginCredential) sessionFactory.getCurrentSession().createQuery( - "from LoginCredential where uuid = :uuid").setString("uuid", uuid.trim()).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, LoginCredential.class, uuid.trim()); } } @@ -555,17 +583,24 @@ public void updateLoginCredential(LoginCredential credential) { * @see org.openmrs.api.db.UserDAO#getUsersByPerson(org.openmrs.Person, boolean) */ @Override - @SuppressWarnings("unchecked") public List getUsersByPerson(Person person, boolean includeRetired) { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(User.class); - crit.add(Restrictions.ne("uuid", Daemon.getDaemonUserUuid())); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(User.class); + Root root = cq.from(User.class); + + List predicates = new ArrayList<>(); + predicates.add(cb.notEqual(root.get("uuid"), Daemon.getDaemonUserUuid())); + if (person != null) { - crit.add(Restrictions.eq("person", person)); + predicates.add(cb.equal(root.get("person"), person)); } if (!includeRetired) { - crit.add(Restrictions.eq("retired", false)); + predicates.add(cb.isFalse(root.get("retired"))); } - return (List) crit.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + return session.createQuery(cq).getResultList(); } /** @@ -576,7 +611,7 @@ public Integer getCountOfUsers(String name, List roles, boolean includeRet String hqlSelectStart = "select count(distinct user) from User as user inner join user.person.names as name "; Query query = createUserSearchQuery(name, roles, includeRetired, hqlSelectStart); - return ((Long) query.uniqueResult()).intValue(); + return ((Long) JpaUtils.getSingleResultOrNull(query)).intValue(); } /** @@ -658,11 +693,11 @@ private Query createUserSearchQuery(String name, List roles, boolean inclu Query query = sessionFactory.getCurrentSession().createQuery(hql.toString()); query.setParameter("DAEMON_USER_UUID", Daemon.getDaemonUserUuid()); for (Map.Entry e : namesMap.entrySet()) { - query.setString(e.getKey(), e.getValue()); + query.setParameter(e.getKey(), e.getValue()); } if (searchOnRoles) { - query.setParameterList("roleList", roles); + query.setParameter("roleList", roles); } return query; diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateUtil.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateUtil.java index a94eaa606dea..2500ceeed103 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateUtil.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateUtil.java @@ -11,23 +11,31 @@ import java.sql.Connection; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Subquery; + import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; import org.hibernate.Hibernate; +import org.hibernate.ScrollMode; +import org.hibernate.ScrollableResults; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Conjunction; -import org.hibernate.criterion.DetachedCriteria; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Property; -import org.hibernate.criterion.Restrictions; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.PostgreSQL82Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.proxy.HibernateProxy; import org.openmrs.Location; +import org.openmrs.LocationAttribute; +import org.openmrs.api.db.DAOException; import org.openmrs.attribute.AttributeType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -141,31 +149,34 @@ public static String escapeSqlWildcards(String oldString, Connection connection) return oldString; } } - + /** - * Adds attribute value criteria to the given criteria query - * - * @param criteria the criteria - * @param serializedAttributeValues the serialized attribute values - * @param the attribute type + * Constructs a list of predicates for attribute value criteria for use in a JPA Criteria query. + * + * @param cb The CriteriaBuilder used to construct the CriteriaQuery + * @param locationRoot The root of the CriteriaQuery for the Location entity + * @param serializedAttributeValues A map of AttributeType to serialized attribute values + * @param The type of the attribute + * @return A list of Predicate objects for use in a CriteriaQuery */ - public static void addAttributeCriteria(Criteria criteria, - Map serializedAttributeValues) { - Conjunction conjunction = Restrictions.conjunction(); - int a = 0; + public static List getAttributePredicate(CriteriaBuilder cb, + Root locationRoot, Map serializedAttributeValues) { + List predicates = new ArrayList<>(); for (Map.Entry entry : serializedAttributeValues.entrySet()) { - String alias = "attributes" + (a++); - DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Location.class).setProjection(Projections.id()); - detachedCriteria.createAlias("attributes", alias); - detachedCriteria.add(Restrictions.eq(alias + ".attributeType", entry.getKey())); - detachedCriteria.add(Restrictions.eq(alias + ".valueReference", entry.getValue())); - detachedCriteria.add(Restrictions.eq(alias + ".voided", false)); + Subquery subquery = cb.createQuery().subquery(Integer.class); + Root locationSubRoot = subquery.from(Location.class); + Join attributeJoin = locationSubRoot.join("attributes"); - conjunction.add(Property.forName("id").in(detachedCriteria)); + Predicate[] attributePredicates = new Predicate[] { cb.equal(attributeJoin.get("attributeType"), entry.getKey()), + cb.equal(attributeJoin.get("valueReference"), entry.getValue()), + cb.isFalse(attributeJoin.get("voided")) }; + + subquery.select(locationSubRoot.get("locationId")).where(attributePredicates); + predicates.add(cb.in(locationRoot.get("locationId")).value(subquery)); } - criteria.add(conjunction); + return predicates; } /** @@ -188,4 +199,43 @@ public static T getRealObjectFromProxy(T persistentObject) { return persistentObject; } + + /** + * Retrieves a unique entity by its UUID. + * + * @param sessionFactory the session factory to create sessions. + * @param entityClass the class of the entity to retrieve. + * @param uuid the UUID of the entity. + * @return the entity if found, null otherwise. + * @throws DAOException if there's an issue in data access. + */ + public static T getUniqueEntityByUUID(SessionFactory sessionFactory, Class entityClass, String uuid) throws DAOException { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(entityClass); + Root root = query.from(entityClass); + + query.where(cb.equal(root.get("uuid"), uuid)); + return session.createQuery(query).uniqueResult(); + } + + /** + * Creates a ScrollableResults instance for the given entity type with the specified fetch size. + * + * @param sessionFactory the session factory to create sessions. + * @param type the class type of the entity for which the ScrollableResults is created. + * @param fetchSize the number of rows to fetch in a batch. + * @return ScrollableResults instance for batch processing. + */ + public static ScrollableResults getScrollableResult(SessionFactory sessionFactory, Class type, int fetchSize) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(type); + Root root = criteriaQuery.from(type); + criteriaQuery.select(root); + + return session.createQuery(criteriaQuery) + .setFetchSize(fetchSize) + .scroll(ScrollMode.FORWARD_ONLY); + } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateVisitDAO.java b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateVisitDAO.java index fd19e483dd30..55a1e3181e53 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/HibernateVisitDAO.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/HibernateVisitDAO.java @@ -9,18 +9,19 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.commons.collections.CollectionUtils; -import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; import org.openmrs.Concept; import org.openmrs.Location; import org.openmrs.Patient; @@ -28,7 +29,6 @@ import org.openmrs.VisitAttribute; import org.openmrs.VisitAttributeType; import org.openmrs.VisitType; -import org.openmrs.api.APIException; import org.openmrs.api.db.DAOException; import org.openmrs.api.db.VisitDAO; import org.springframework.transaction.annotation.Transactional; @@ -55,19 +55,31 @@ private Session getCurrentSession() { * @see org.openmrs.api.db.VisitDAO#getAllVisitTypes() */ @Override - @SuppressWarnings("unchecked") @Transactional(readOnly = true) - public List getAllVisitTypes() throws APIException { - return getCurrentSession().createCriteria(VisitType.class).list(); + public List getAllVisitTypes() { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(VisitType.class); + cq.from(VisitType.class); + + return session.createQuery(cq).getResultList(); } /** * @see org.openmrs.api.db.VisitDAO#getAllVisitTypes(boolean) */ @Override - public List getAllVisitTypes(boolean includeRetired) throws DAOException { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(VisitType.class); - return includeRetired ? criteria.list() : criteria.add(Restrictions.eq("retired", includeRetired)).list(); + public List getAllVisitTypes(boolean includeRetired) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(VisitType.class); + Root root = cq.from(VisitType.class); + + if (!includeRetired) { + cq.where(cb.equal(root.get("retired"), includeRetired)); + } + + return session.createQuery(cq).getResultList(); } /** @@ -76,7 +88,7 @@ public List getAllVisitTypes(boolean includeRetired) throws DAOExcept @Override @Transactional(readOnly = true) public VisitType getVisitType(Integer visitTypeId) { - return (VisitType) sessionFactory.getCurrentSession().get(VisitType.class, visitTypeId); + return sessionFactory.getCurrentSession().get(VisitType.class, visitTypeId); } /** @@ -85,21 +97,24 @@ public VisitType getVisitType(Integer visitTypeId) { @Override @Transactional(readOnly = true) public VisitType getVisitTypeByUuid(String uuid) { - return (VisitType) sessionFactory.getCurrentSession().createQuery("from VisitType vt where vt.uuid = :uuid") - .setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, VisitType.class, uuid); } /** * @see org.openmrs.api.db.VisitDAO#getVisitTypes(java.lang.String) */ @Override - @SuppressWarnings("unchecked") @Transactional(readOnly = true) public List getVisitTypes(String fuzzySearchPhrase) { - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(VisitType.class); - criteria.add(Restrictions.ilike("name", fuzzySearchPhrase, MatchMode.ANYWHERE)); - criteria.addOrder(Order.asc("name")); - return criteria.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(VisitType.class); + Root root = cq.from(VisitType.class); + + cq.where(cb.like(cb.lower(root.get("name")), MatchMode.ANYWHERE.toLowerCasePattern(fuzzySearchPhrase))); + cq.orderBy(cb.asc(root.get("name"))); + + return session.createQuery(cq).getResultList(); } /** @@ -127,7 +142,7 @@ public void purgeVisitType(VisitType visitType) { @Override @Transactional(readOnly = true) public Visit getVisit(Integer visitId) throws DAOException { - return (Visit) getCurrentSession().get(Visit.class, visitId); + return getCurrentSession().get(Visit.class, visitId); } /** @@ -136,8 +151,7 @@ public Visit getVisit(Integer visitId) throws DAOException { @Override @Transactional(readOnly = true) public Visit getVisitByUuid(String uuid) throws DAOException { - return (Visit) getCurrentSession().createQuery("from Visit v where v.uuid = :uuid").setString("uuid", uuid) - .uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, Visit.class, uuid); } /** @@ -164,75 +178,81 @@ public void deleteVisit(Visit visit) throws DAOException { * java.util.Collection, java.util.Collection, java.util.Date, java.util.Date, * java.util.Date, java.util.Date, java.util.Map, boolean, boolean) */ - @SuppressWarnings("unchecked") @Override @Transactional(readOnly = true) public List getVisits(Collection visitTypes, Collection patients, Collection locations, Collection indications, Date minStartDatetime, Date maxStartDatetime, Date minEndDatetime, Date maxEndDatetime, final Map serializedAttributeValues, - boolean includeInactive, boolean includeVoided) throws DAOException { - - Criteria criteria = getCurrentSession().createCriteria(Visit.class); - - if (visitTypes != null) { - criteria.add(Restrictions.in("visitType", visitTypes)); + boolean includeInactive, boolean includeVoided) { + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Visit.class); + Root root = cq.from(Visit.class); + + List predicates = new ArrayList<>(); + + if (visitTypes != null && !visitTypes.isEmpty()) { + predicates.add(root.get("visitType").in(visitTypes)); } - if (patients != null) { - criteria.add(Restrictions.in("patient", patients)); + if (patients != null && !patients.isEmpty()) { + predicates.add(root.get("patient").in(patients)); } - if (locations != null) { - criteria.add(Restrictions.in("location", locations)); + if (locations != null && !locations.isEmpty()) { + predicates.add(root.get("location").in(locations)); } - if (indications != null) { - criteria.add(Restrictions.in("indication", indications)); + if (indications != null && !indications.isEmpty()) { + predicates.add(root.get("indication").in(indications)); } - if (minStartDatetime != null) { - criteria.add(Restrictions.ge("startDatetime", minStartDatetime)); + predicates.add(cb.greaterThanOrEqualTo(root.get("startDatetime"), minStartDatetime)); } if (maxStartDatetime != null) { - criteria.add(Restrictions.le("startDatetime", maxStartDatetime)); + predicates.add(cb.lessThanOrEqualTo(root.get("startDatetime"), maxStartDatetime)); } - + // active visits have null end date, so it doesn't make sense to search against it if include inactive is set to false if (!includeInactive) { // the user only asked for currently active visits, so stop time needs to be null or after right now - criteria.add(Restrictions.or(Restrictions.isNull("stopDatetime"), Restrictions.gt("stopDatetime", new Date()))); + predicates.add(cb.or(cb.isNull(root.get("stopDatetime")), cb.greaterThan(root.get("stopDatetime"), new Date()))); } else { if (minEndDatetime != null) { - criteria.add(Restrictions.or(Restrictions.isNull("stopDatetime"), Restrictions.ge("stopDatetime", - minEndDatetime))); + predicates.add(cb.or(cb.isNull(root.get("stopDatetime")), cb.greaterThanOrEqualTo(root.get("stopDatetime"), + minEndDatetime))); } if (maxEndDatetime != null) { - criteria.add(Restrictions.le("stopDatetime", maxEndDatetime)); + predicates.add(cb.lessThanOrEqualTo(root.get("stopDatetime"), maxEndDatetime)); } } - + if (!includeVoided) { - criteria.add(Restrictions.eq("voided", false)); + predicates.add(cb.isFalse(root.get("voided"))); } - - criteria.addOrder(Order.desc("startDatetime")); - criteria.addOrder(Order.desc("visitId")); - - List visits = criteria.list(); - + + cq.where(predicates.toArray(new Predicate[]{})) + .orderBy(cb.desc(root.get("startDatetime")), cb.desc(root.get("visitId"))); + + List visits = session.createQuery(cq).getResultList(); + if (serializedAttributeValues != null) { CollectionUtils.filter(visits, new AttributeMatcherPredicate( - serializedAttributeValues)); + serializedAttributeValues)); } - + return visits; } /** * @see org.openmrs.api.db.VisitDAO#getAllVisitAttributeTypes() */ - @SuppressWarnings("unchecked") @Override @Transactional(readOnly = true) public List getAllVisitAttributeTypes() { - return getCurrentSession().createCriteria(VisitAttributeType.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(VisitAttributeType.class); + cq.from(VisitAttributeType.class); + + return session.createQuery(cq).getResultList(); } /** @@ -241,7 +261,7 @@ public List getAllVisitAttributeTypes() { @Override @Transactional(readOnly = true) public VisitAttributeType getVisitAttributeType(Integer id) { - return (VisitAttributeType) getCurrentSession().get(VisitAttributeType.class, id); + return getCurrentSession().get(VisitAttributeType.class, id); } /** @@ -250,8 +270,7 @@ public VisitAttributeType getVisitAttributeType(Integer id) { @Override @Transactional(readOnly = true) public VisitAttributeType getVisitAttributeTypeByUuid(String uuid) { - return (VisitAttributeType) getCurrentSession().createCriteria(VisitAttributeType.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, VisitAttributeType.class, uuid); } /** @@ -279,8 +298,7 @@ public void deleteVisitAttributeType(VisitAttributeType visitAttributeType) { @Override @Transactional(readOnly = true) public VisitAttribute getVisitAttributeByUuid(String uuid) { - return (VisitAttribute) getCurrentSession().createCriteria(VisitAttribute.class).add(Restrictions.eq("uuid", uuid)) - .uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, VisitAttribute.class, uuid); } /** @@ -288,18 +306,28 @@ public VisitAttribute getVisitAttributeByUuid(String uuid) { */ @Override public Visit getNextVisit(Visit previousVisit, Collection visitTypes, Date maximumStartDate) { - Criteria criteria = getCurrentSession().createCriteria(Visit.class); - criteria.add(Restrictions.eq("voided", false)).add( - Restrictions.gt("visitId", (previousVisit != null) ? previousVisit.getVisitId() : 0)).addOrder( - Order.asc("visitId")).add(Restrictions.isNull("stopDatetime")).setMaxResults(1); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Visit.class); + Root root = cq.from(Visit.class); + + List predicates = new ArrayList<>(); + + predicates.add(cb.isFalse(root.get("voided"))); + predicates.add(cb.greaterThan(root.get("visitId"), (previousVisit != null) ? previousVisit.getVisitId() : 0)); + predicates.add(cb.isNull(root.get("stopDatetime"))); + if (maximumStartDate != null) { - criteria.add(Restrictions.le("startDatetime", maximumStartDate)); + predicates.add(cb.lessThanOrEqualTo(root.get("startDatetime"), maximumStartDate)); } - + if (CollectionUtils.isNotEmpty(visitTypes)) { - criteria.add(Restrictions.in("visitType", visitTypes)); + predicates.add(root.get("visitType").in(visitTypes)); } - - return (Visit) criteria.uniqueResult(); + + cq.where(predicates.toArray(new Predicate[]{})) + .orderBy(cb.asc(root.get("visitId"))); + + return session.createQuery(cq).setMaxResults(1).uniqueResult(); } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/JpaUtils.java b/api/src/main/java/org/openmrs/api/db/hibernate/JpaUtils.java new file mode 100644 index 000000000000..45bd3ae7550c --- /dev/null +++ b/api/src/main/java/org/openmrs/api/db/hibernate/JpaUtils.java @@ -0,0 +1,35 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.api.db.hibernate; + +import javax.persistence.NoResultException; +import javax.persistence.NonUniqueResultException; +import javax.persistence.Query; + +public class JpaUtils { + + /** + * Tries to get a single result from a JPA query, similar to Hibernate's uniqueResult. + * Returns null if no result is found, the single result if one result is found, + * and throws an exception if more than one result is found. + * + * @param query the JPA query to execute + * @param the type of the query result + * @return the single result or null if no result is found + * @throws NonUniqueResultException if more than one result is found + */ + public static T getSingleResultOrNull(Query query) { + try { + return (T) query.getSingleResult(); + } catch (NoResultException e) { + return null; + } + } +} diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/MatchMode.java b/api/src/main/java/org/openmrs/api/db/hibernate/MatchMode.java new file mode 100644 index 000000000000..180fee7926a5 --- /dev/null +++ b/api/src/main/java/org/openmrs/api/db/hibernate/MatchMode.java @@ -0,0 +1,43 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.api.db.hibernate; + +public enum MatchMode { + START, + END, + ANYWHERE, + EXACT; + + public String toCaseSensitivePattern(String str) { + return toPatternInternal(str, false); + } + + public String toLowerCasePattern(String str) { + return toPatternInternal(str, true); + } + + private String toPatternInternal(String str, boolean caseInsensitive) { + if (str == null) { + return null; + } + String processedStr = caseInsensitive ? str.toLowerCase() : str; + switch (this) { + case START: + return processedStr + "%"; + case END: + return "%" + processedStr; + case ANYWHERE: + return "%" + processedStr + "%"; + case EXACT: + default: + return processedStr; + } + } +} diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/PatientSearchCriteria.java b/api/src/main/java/org/openmrs/api/db/hibernate/PatientSearchCriteria.java index 3d8e689b453e..b8248c757940 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/PatientSearchCriteria.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/PatientSearchCriteria.java @@ -9,29 +9,27 @@ */ package org.openmrs.api.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Predicate; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.hibernate.Criteria; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Conjunction; -import org.hibernate.criterion.CriteriaSpecification; -import org.hibernate.criterion.Criterion; -import org.hibernate.criterion.LogicalExpression; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; -import org.hibernate.criterion.SimpleExpression; -import org.hibernate.type.StringType; +import org.openmrs.Encounter; +import org.openmrs.Patient; +import org.openmrs.PatientIdentifier; import org.openmrs.PatientIdentifierType; +import org.openmrs.PersonName; import org.openmrs.api.AdministrationService; import org.openmrs.api.context.Context; +import org.openmrs.attribute.Attribute; +import org.openmrs.attribute.AttributeType; import org.openmrs.util.OpenmrsConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * The PatientSearchCriteria class. It has API to return a criteria from the Patient Name and @@ -41,57 +39,55 @@ */ @Deprecated public class PatientSearchCriteria { - - private static final Logger log = LoggerFactory.getLogger(PatientSearchCriteria.class); - + private final SessionFactory sessionFactory; - - private final Criteria criteria; - - private PersonSearchCriteria personSearchCriteria; - + + private final PersonSearchCriteria personSearchCriteria; + /** * @param sessionFactory - * @param criteria */ - public PatientSearchCriteria(SessionFactory sessionFactory, Criteria criteria) { + public PatientSearchCriteria(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; - this.criteria = criteria; this.personSearchCriteria = new PersonSearchCriteria(); } - + /** - * Prepare a hibernate criteria for searching patients by name and/or identifier. - * - * The visibility of this method remains public in order not to break OpenMRS modules that use this method. - * - * Instead of calling this method consider using {@link org.openmrs.api.PatientService} or + * Prepare a {@link QueryResult} for searching patients by name and/or identifier. The visibility of + * this method remains public in order not to break OpenMRS modules that use this method. Instead of + * calling this method consider using {@link org.openmrs.api.PatientService} or * {@link org.openmrs.api.db.PatientDAO}. * + * @param cb + * @param patientJoin * @param name * @param identifier * @param identifierTypes * @param matchIdentifierExactly - * @param searchOnNamesOrIdentifiers specifies if the logic should find patients that match the - * name or identifier otherwise find patients that match both the name and identifier - * @return {@link Criteria} + * @param searchOnNamesOrIdentifiers specifies if the logic should find patients that match the name + * or identifier otherwise find patients that match both the name and identifier + * @return {@link QueryResult} */ - public Criteria prepareCriteria(String name, String identifier, List identifierTypes, - boolean matchIdentifierExactly, boolean orderByNames, boolean searchOnNamesOrIdentifiers) { + public QueryResult prepareCriteria(CriteriaBuilder cb, Join patientJoin, String name, + String identifier, List identifierTypes, boolean matchIdentifierExactly, + boolean orderByNames, boolean searchOnNamesOrIdentifiers) { + QueryResult queryResult = new QueryResult(); PatientSearchMode patientSearchMode = getSearchMode(name, identifier, identifierTypes, searchOnNamesOrIdentifiers); - + + List predicates = new ArrayList<>(); + Join nameJoin; switch (patientSearchMode) { case PATIENT_SEARCH_BY_NAME: - addAliasForName(criteria, orderByNames); - criteria.add(prepareCriterionForName(name)); + nameJoin = addAliasForName(cb, patientJoin, orderByNames, queryResult); + predicates.add(preparePredicateForName(cb, nameJoin, name)); break; - + case PATIENT_SEARCH_BY_IDENTIFIER: - addAliasForIdentifiers(criteria); - criteria.add(prepareCriterionForIdentifier(identifier, identifierTypes, matchIdentifierExactly)); + Join identifierJoin = addAliasForIdentifiers(patientJoin); + predicates.add(preparePredicateForIdentifier(cb, identifierJoin, identifier, identifierTypes, matchIdentifierExactly)); break; - + case PATIENT_SEARCH_BY_NAME_OR_IDENTIFIER: // If only name *or* identifier is provided as a search parameter, @@ -101,62 +97,72 @@ public Criteria prepareCriteria(String name, String identifier, List idsJoin = addAliasForIdentifiers(patientJoin); + predicates.add((cb.or( + preparePredicateForName(cb, nameJoin, name), + preparePredicateForIdentifier(cb, idsJoin, identifier, identifierTypes, matchIdentifierExactly)) + )); break; - + case PATIENT_SEARCH_BY_NAME_AND_IDENTIFIER: - addAliasForName(criteria, orderByNames); - addAliasForIdentifiers(criteria); - criteria.add(prepareCriterionForName(name)); - criteria.add(prepareCriterionForIdentifier(identifier, identifierTypes, matchIdentifierExactly)); + nameJoin = addAliasForName(cb, patientJoin, orderByNames, queryResult); + Join idJoin = addAliasForIdentifiers(patientJoin); + predicates.add((cb.and( + preparePredicateForName(cb, nameJoin, name), + preparePredicateForIdentifier(cb, idJoin, identifier, identifierTypes, matchIdentifierExactly)) + )); break; - + default: break; } - - criteria.add(Restrictions.eq("voided", false)); - criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); - - log.debug(criteria.toString()); - - return criteria; + + predicates.add(cb.isFalse(patientJoin.get("voided"))); + + queryResult.addPredicates(predicates); + + return queryResult; } - + /** - * Provides a Hibernate criteria object for searching patients by name, identifier or searchable attribute. - * + * Provides a {@link QueryResult} object for searching patients by name, identifier or searchable attribute. + * * The visibility of this method is "default" as this method should NOT be called directly by classes other * than org.openmrs.api.db.hibernate.HibernatePatientDAO. - * + * * Instead of calling this method consider using {@link org.openmrs.api.PatientService} or * {@link org.openmrs.api.db.PatientDAO}. * - * @param query defines search parameters + * @param cb the CriteriaBuilder to build the criteria + * @param patientJoin the join from Encounter to Patient + * @param query defines search parameters * @param includeVoided true/false whether or not to included voided patients - * @return criteria for searching by name OR identifier OR searchable attributes + * @return QueryResult for searching by name OR identifier OR searchable attributes */ - Criteria prepareCriteria(String query, boolean includeVoided) { - addAliasForName(criteria, true); - personSearchCriteria.addAliasForAttribute(criteria); - addAliasForIdentifiers(criteria); - criteria.add(Restrictions.disjunction().add(prepareCriterionForName(query, includeVoided)).add( - prepareCriterionForAttribute(query, includeVoided)).add( - prepareCriterionForIdentifier(query, new ArrayList<>(), false, includeVoided))); + QueryResult prepareCriteria(CriteriaBuilder cb, Join patientJoin, String query, boolean includeVoided) { + QueryResult queryResult = new QueryResult(); + List predicates = new ArrayList<>(); + + Join nameJoin = addAliasForName(cb, patientJoin, true, queryResult); + Join attributeJoin = personSearchCriteria.addAliasForAttribute(patientJoin); + Join attributeTypeJoin = personSearchCriteria.addAliasForAttributeType(attributeJoin); + Join idsJoin = addAliasForIdentifiers(patientJoin); + predicates.add(cb.or( + preparePredicateForName(cb, nameJoin, query, includeVoided), + prepareCriterionForAttribute(cb, attributeJoin, attributeTypeJoin, query, includeVoided), + preparePredicateForIdentifier(cb, idsJoin, query, new ArrayList<>(), false, includeVoided))); if (!includeVoided) { - criteria.add(Restrictions.eq("voided", false)); + predicates.add(cb.isFalse(patientJoin.get("voided"))); } - criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); - log.debug(criteria.toString()); - return criteria; + + queryResult.addPredicates(predicates); + return queryResult; } - + /** - * Provides a Hibernate criteria object for searching patients by name, identifier or searchable attribute. + * Provides a {@link QueryResult} object for searching patients by name, identifier or searchable attribute. * * The visibility of this method is "default" as this method should NOT be called directly by classes other * than org.openmrs.api.db.hibernate.HibernatePatientDAO. @@ -164,48 +170,58 @@ Criteria prepareCriteria(String query, boolean includeVoided) { * Instead of calling this method consider using {@link org.openmrs.api.PatientService} or * {@link org.openmrs.api.db.PatientDAO}. * + * @param cb the CriteriaBuilder to build the criteria + * @param patientJoin the join from Encounter to Patient * @param query defines search parameters - * @return criteria for searching by name OR identifier OR searchable attributes + * @return QueryResult for searching by name OR identifier OR searchable attributes */ - Criteria prepareCriteria(String query) { - return prepareCriteria(query, false); + QueryResult prepareCriteria(CriteriaBuilder cb, Join patientJoin, String query) { + return prepareCriteria(cb, patientJoin, query, false); } - + /** - * @param query defines search parameters - * @param matchExactly - * @param orderByNames + * @param query defines search parameters + * + * @param cb the CriteriaBuilder to build the criteria + * @param patientJoin the join from Encounter to Patient + * @param matchExactly true/false whether to perform an exact match on names + * @param orderByNames true/false whether to order by names * @param includeVoided true/false whether or not to included voided patients - * @return criteria for searching by name OR identifier OR searchable attributes + * @return QueryResult for searching by name OR identifier OR searchable attributes */ - Criteria prepareCriteria(String query, Boolean matchExactly, boolean orderByNames, boolean includeVoided) { - addAliasForName(criteria, orderByNames); + QueryResult prepareCriteria(CriteriaBuilder cb, Join patientJoin, String query, Boolean matchExactly, boolean orderByNames, boolean includeVoided) { + QueryResult queryResult = new QueryResult(); + List predicates = new ArrayList<>(); + Join nameJoin = addAliasForName(cb, patientJoin, orderByNames, queryResult); + if (matchExactly == null) { - criteria.add(Restrictions.conjunction().add(prepareCriterionForName(query, null, includeVoided)).add( - Restrictions.not(prepareCriterionForName(query, true, includeVoided))).add( - Restrictions.not(prepareCriterionForName(query, false, includeVoided)))); + predicates.add(cb.and( + preparePredicateForName(cb, nameJoin, query, null, includeVoided), + preparePredicateForName(cb, nameJoin, query, true, includeVoided), + cb.not(preparePredicateForName(cb, nameJoin, query, false, includeVoided)))); } else if (!matchExactly) { - criteria.add(prepareCriterionForName(query, false, includeVoided)); + predicates.add(preparePredicateForName(cb, nameJoin, query, false, includeVoided)); } else { - personSearchCriteria.addAliasForAttribute(criteria); - addAliasForIdentifiers(criteria); - - criteria.add(Restrictions.disjunction().add(prepareCriterionForName(query, true, includeVoided)).add( - prepareCriterionForAttribute(query, includeVoided)).add( - prepareCriterionForIdentifier(query, new ArrayList<>(), false, includeVoided))); + Join attributeJoin = personSearchCriteria.addAliasForAttribute(patientJoin); + Join attributeTypeJoin = personSearchCriteria.addAliasForAttributeType(attributeJoin); + Join idsJoin = addAliasForIdentifiers(patientJoin); + + predicates.add(cb.or( + preparePredicateForName(cb, nameJoin, query, true, includeVoided), + prepareCriterionForAttribute(cb, attributeJoin, attributeTypeJoin, query, includeVoided), + preparePredicateForIdentifier(cb, idsJoin, query, new ArrayList<>(), false, includeVoided)) + ); } - + if (!includeVoided) { - criteria.add(Restrictions.eq("voided", false)); + predicates.add(cb.isFalse(patientJoin.get("voided"))); } - criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); - - log.debug(criteria.toString()); - return criteria; + queryResult.addPredicates(predicates); + return queryResult; } - + /** * Should return source value when target is blank * Should return target value when target is non-blank @@ -216,7 +232,7 @@ String copySearchParameter(String source, String target) { } return target; } - + /** * Should identify search by name * Should identify search by identifier @@ -230,135 +246,151 @@ PatientSearchMode getSearchMode(String name, String identifier, List !(A&&B) // if (StringUtils.isBlank(name) && !(StringUtils.isBlank(identifier) && CollectionUtils.isEmpty(identifierTypes))) { return PatientSearchMode.PATIENT_SEARCH_BY_IDENTIFIER; } - + return PatientSearchMode.PATIENT_SEARCH_BY_NAME_AND_IDENTIFIER; } - private void addAliasForName(Criteria criteria, boolean orderByNames) { - criteria.createAlias("names", "name"); + private Join addAliasForName(CriteriaBuilder cb, Join patientJoin, boolean orderByNames, QueryResult queryResult) { + Join nameJoin = patientJoin.join("names"); if (orderByNames) { - criteria.addOrder(Order.asc("name.givenName")); - criteria.addOrder(Order.asc("name.middleName")); - criteria.addOrder(Order.asc("name.familyName")); + queryResult.addOrder(cb.asc(nameJoin.get("givenName"))); + queryResult.addOrder(cb.asc(nameJoin.get("middleName"))); + queryResult.addOrder(cb.asc(nameJoin.get("familyName"))); } + return nameJoin; } - - private void addAliasForIdentifiers(Criteria criteria) { - criteria.createAlias("identifiers", "ids", CriteriaSpecification.LEFT_JOIN); + + private Join addAliasForIdentifiers(Join patientJoin) { + return patientJoin.join("identifiers", JoinType.LEFT); } - + + /** * Utility method to add identifier expression to an existing criteria * + * @param cb the CriteriaBuilder to build the criteria + * @param idsJoin the join from Patient to PatientIdentifier * @param identifier * @param identifierTypes * @param matchIdentifierExactly * @param includeVoided true/false whether or not to included voided patients */ - private Criterion prepareCriterionForIdentifier(String identifier, List identifierTypes, - boolean matchIdentifierExactly, boolean includeVoided) { + private Predicate preparePredicateForIdentifier(CriteriaBuilder cb, Join idsJoin, + String identifier, List identifierTypes, boolean matchIdentifierExactly, + boolean includeVoided) { identifier = HibernateUtil.escapeSqlWildcards(identifier, sessionFactory); - Conjunction conjunction = Restrictions.conjunction(); - + List predicates = new ArrayList<>(); + if (!includeVoided) { - conjunction.add(Restrictions.eq("ids.voided", false)); + predicates.add(cb.isFalse(idsJoin.get("voided"))); } // do the identifier restriction if (identifier != null) { // if the user wants an exact search, match on that. if (matchIdentifierExactly) { - SimpleExpression matchIdentifier = Restrictions.eq("ids.identifier", identifier); if (Context.getAdministrationService().isDatabaseStringComparisonCaseSensitive()) { - matchIdentifier.ignoreCase(); + predicates.add(cb.equal(cb.lower(idsJoin.get("identifier")), identifier.toLowerCase())); + } else { + predicates.add(cb.equal(idsJoin.get("identifier"), identifier)); } - conjunction.add(matchIdentifier); } else { AdministrationService adminService = Context.getAdministrationService(); String regex = adminService.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_REGEX, ""); String patternSearch = adminService.getGlobalProperty( - OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_SEARCH_PATTERN, ""); - + OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_SEARCH_PATTERN, ""); + // remove padding from identifier search string if (Pattern.matches("^\\^.{1}\\*.*$", regex)) { identifier = removePadding(identifier, regex); } - + if (org.springframework.util.StringUtils.hasLength(patternSearch)) { - conjunction.add(splitAndGetSearchPattern(identifier, patternSearch)); + predicates.add(splitAndGetSearchPattern(cb, idsJoin, identifier, patternSearch)); } // if the regex is empty, default to a simple "like" search or if // we're in hsql world, also only do the simple like search (because // hsql doesn't know how to deal with 'regexp' else if ("".equals(regex) || HibernateUtil.isHSQLDialect(sessionFactory)) { - conjunction.add(getCriterionForSimpleSearch(identifier, adminService)); + predicates.add(getPredicateForSimpleSearch(cb, idsJoin, identifier, adminService)); } // if the regex is present, search on that else { regex = replaceSearchString(regex, identifier); - conjunction.add(Restrictions.sqlRestriction("identifier regexp ?", regex, StringType.INSTANCE)); + predicates.add(cb.isTrue(cb.function("regexp", Boolean.class, idsJoin.get("identifier"), + cb.literal(regex)))); } } } - + // do the type restriction if (!CollectionUtils.isEmpty(identifierTypes)) { - criteria.add(Restrictions.in("ids.identifierType", identifierTypes)); + predicates.add(idsJoin.get("identifierType").in(identifierTypes)); } - - return conjunction; + + return cb.and(predicates.toArray(predicates.toArray(new Predicate[]{}))); } - + /** * Utility method to add identifier expression to an existing criteria * + * @param cb the CriteriaBuilder to build the criteria + * @param idsJoin the join from Patient to PatientIdentifier * @param identifier * @param identifierTypes * @param matchIdentifierExactly */ - private Criterion prepareCriterionForIdentifier(String identifier, List identifierTypes, - boolean matchIdentifierExactly) { - return prepareCriterionForIdentifier(identifier, identifierTypes, matchIdentifierExactly, false); + private Predicate preparePredicateForIdentifier(CriteriaBuilder cb, Join idsJoin, + String identifier, List identifierTypes, boolean matchIdentifierExactly) { + return preparePredicateForIdentifier(cb, idsJoin, identifier, identifierTypes, matchIdentifierExactly, false); } - + /** * Utility method to add prefix and suffix like expression * + * @param cb the CriteriaBuilder to build the criteria + * @param idsJoin the join from Patient to PatientIdentifier * @param identifier * @param adminService */ - private Criterion getCriterionForSimpleSearch(String identifier, AdministrationService adminService) { + private Predicate getPredicateForSimpleSearch(CriteriaBuilder cb, Join idsJoin, + String identifier, AdministrationService adminService) { String prefix = adminService.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_PREFIX, ""); String suffix = adminService.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_IDENTIFIER_SUFFIX, ""); - return Restrictions.ilike("ids.identifier", prefix + identifier + suffix); + String matchPattern = (prefix + identifier + suffix).toLowerCase(); + return cb.like(cb.lower(idsJoin.get("identifier")), matchPattern); } - + /** * Utility method to add search pattern expression to identifier. * + * @param cb + * @param idsJoin * @param identifier * @param patternSearch */ - private Criterion splitAndGetSearchPattern(String identifier, String patternSearch) { - // split the pattern before replacing in case the user searched on a comma - List searchPatterns = new ArrayList<>(); + private Predicate splitAndGetSearchPattern(CriteriaBuilder cb, Join idsJoin, + String identifier, String patternSearch) { + CriteriaBuilder.In inClause = cb.in(idsJoin.get("identifier")); // replace the @SEARCH@, etc in all elements for (String pattern : patternSearch.split(",")) { - searchPatterns.add(replaceSearchString(pattern, identifier)); + inClause.value(replaceSearchString(pattern, identifier)); } - return Restrictions.in("ids.identifier", searchPatterns); + return inClause; } - + + + /** * Utility method to remove padding from the identifier. * @@ -372,63 +404,62 @@ private String removePadding(String identifier, String regex) { identifier = pattern.matcher(identifier).replaceFirst(""); return identifier; } - + /** * Utility method to add name expressions to criteria. * + * @param cb + * @param nameJoin * @param name * @param matchExactly * @param includeVoided true/false whether or not to included voided patients */ - private Criterion prepareCriterionForName(String name, Boolean matchExactly, boolean includeVoided) { + private Predicate preparePredicateForName(CriteriaBuilder cb, Join nameJoin, String name, + Boolean matchExactly, boolean includeVoided) { name = HibernateUtil.escapeSqlWildcards(name, sessionFactory); - - Conjunction conjunction = Restrictions.conjunction(); + List predicates = new ArrayList<>(); + String[] nameParts = getQueryParts(name); if (nameParts.length > 0) { StringBuilder multiName = new StringBuilder(nameParts[0]); - + for (int i = 0; i < nameParts.length; i++) { String singleName = nameParts[i]; - - if (singleName != null && singleName.length() > 0) { - Criterion singleNameCriterion = getCriterionForName(singleName, matchExactly, includeVoided); - Criterion criterion = singleNameCriterion; - + + if (singleName != null && !singleName.isEmpty()) { + Predicate singleNamePredicate = getPredicateForName(cb, nameJoin, singleName, matchExactly, includeVoided); + if (i > 0) { multiName.append(" "); multiName.append(singleName); - Criterion multiNameCriterion = getCriterionForName(multiName.toString(), matchExactly, includeVoided); - criterion = Restrictions.or(singleNameCriterion, multiNameCriterion); + Predicate multiNamePredicate = getPredicateForName(cb, nameJoin, multiName.toString(), matchExactly, includeVoided); + singleNamePredicate = cb.or(singleNamePredicate, multiNamePredicate); } - - conjunction.add(criterion); + + predicates.add(singleNamePredicate); } } } - - return conjunction; + + return cb.and(predicates.toArray(new Predicate[]{})); } - + /** * Utility method to add name expressions to criteria. * + * @param cb the CriteriaBuilder to build the criteria + * @param nameJoin the join from Patient to PersonName * @param name - * @param includeVoided true/false whether or not to included voided patients */ - private Criterion prepareCriterionForName(String name, boolean includeVoided) { - return prepareCriterionForName(name, null, includeVoided); + private Predicate preparePredicateForName(CriteriaBuilder cb, Join nameJoin, String name) { + return preparePredicateForName(cb, nameJoin, name, null, false); } - /** - * Utility method to add name expressions to criteria. - * - * @param name - */ - private Criterion prepareCriterionForName(String name) { - return prepareCriterionForName(name, null, false); + + private Predicate preparePredicateForName(CriteriaBuilder cb, Join nameJoin, String name, boolean includeVoided) { + return preparePredicateForName(cb, nameJoin, name, null, includeVoided); } - + /** * Should process simple space as separator * Should process comma as separator @@ -440,20 +471,20 @@ String[] getQueryParts(String query) { if (query == null) { throw new IllegalArgumentException("query must not be null"); } - + query = query.replace(",", " "); String[] queryPartArray = query.split(" "); - + List queryPartList = new ArrayList<>(); for (String queryPart : queryPartArray) { if (queryPart.trim().length() > 0) { queryPartList.add(queryPart); } } - + return queryPartList.toArray(new String[0]); } - + /** * Returns a criteria object comparing the given string to each part of the name.
*
@@ -467,100 +498,130 @@ String[] getQueryParts(String query) { * Except when the name provided is less than min characters (usually 3) then we will look for * an EXACT match by default * + * @param cb the CriteriaBuilder to build the criteria + * @param nameJoin the join from Patient to PersonName * @param name * @param matchExactly * @param includeVoided true/false whether or not to included voided patients - * @return {@link LogicalExpression} + * @return {@link Predicate} */ - private Criterion getCriterionForName(String name, Boolean matchExactly, boolean includeVoided) { + private Predicate getPredicateForName(CriteriaBuilder cb, Join nameJoin, String name, Boolean matchExactly, boolean includeVoided) { if (isShortName(name)) { - return getCriterionForShortName(name, includeVoided); + return getPredicateForShortName(cb, nameJoin, name, includeVoided); } else { if (matchExactly != null) { if (matchExactly) { - return getCriterionForShortName(name, includeVoided); + return getPredicateForShortName(cb, nameJoin, name, includeVoided); } - return getCriterionForNoExactName(name, includeVoided); + return getPredicateForNoExactName(cb, nameJoin, name, includeVoided); } - return getCriterionForLongName(name, includeVoided); + return getPredicateForLongName(cb, nameJoin, name, includeVoided); } } - + + /** * Should recognise short name * Should recognise long name */ Boolean isShortName(String name) { Integer minChars = Context.getAdministrationService().getGlobalPropertyValue( - OpenmrsConstants.GLOBAL_PROPERTY_MIN_SEARCH_CHARACTERS, - OpenmrsConstants.GLOBAL_PROPERTY_DEFAULT_MIN_SEARCH_CHARACTERS); - + OpenmrsConstants.GLOBAL_PROPERTY_MIN_SEARCH_CHARACTERS, + OpenmrsConstants.GLOBAL_PROPERTY_DEFAULT_MIN_SEARCH_CHARACTERS); + if (name != null && name.length() < minChars) { return Boolean.TRUE; - + } else { return Boolean.FALSE; } } - - private Criterion getCriterionForShortName(String name, boolean includeVoided) { - Criterion criterion = Restrictions.disjunction().add( - Restrictions.conjunction().add(Restrictions.isNotNull("name.givenName")).add( - Restrictions.eq("name.givenName", name).ignoreCase())).add( - Restrictions.conjunction().add(Restrictions.isNotNull("name.middleName")).add( - Restrictions.eq("name.middleName", name).ignoreCase())).add( - Restrictions.conjunction().add(Restrictions.isNotNull("name.familyName")).add( - Restrictions.eq("name.familyName", name).ignoreCase())).add( - Restrictions.conjunction().add(Restrictions.isNotNull("name.familyName2")).add( - Restrictions.eq("name.familyName2", name).ignoreCase())); - + + private Predicate getPredicateForShortName(CriteriaBuilder cb, Join nameJoin, String name, boolean includeVoided) { + Predicate givenNamePredicate = cb.and( + cb.isNotNull(nameJoin.get("givenName")), + cb.equal(cb.lower(nameJoin.get("givenName")), name.toLowerCase()) + ); + + Predicate middleNamePredicate = cb.and( + cb.isNotNull(nameJoin.get("middleName")), + cb.equal(cb.lower(nameJoin.get("middleName")), name.toLowerCase()) + ); + + Predicate familyNamePredicate = cb.and( + cb.isNotNull(nameJoin.get("familyName")), + cb.equal(cb.lower(nameJoin.get("familyName")), name.toLowerCase()) + ); + + Predicate familyName2Predicate = cb.and( + cb.isNotNull(nameJoin.get("familyName2")), + cb.equal(cb.lower(nameJoin.get("familyName2")), name.toLowerCase()) + ); + + Predicate namePredicate = cb.or(givenNamePredicate, middleNamePredicate, familyNamePredicate, familyName2Predicate); + if (!includeVoided) { - return Restrictions.conjunction().add(Restrictions.eq("name.voided", false)).add(criterion); + Predicate nonVoidedPredicate = cb.isFalse(nameJoin.get("voided")); + namePredicate = cb.and(namePredicate, nonVoidedPredicate); } - return criterion; + + return namePredicate; } - - private Criterion getCriterionForLongName(String name, boolean includeVoided) { - MatchMode matchMode = getMatchMode(); - Criterion criterion = Restrictions.disjunction().add(Restrictions.like("name.givenName", name, matchMode)).add( - Restrictions.like("name.middleName", name, matchMode)) - .add(Restrictions.like("name.familyName", name, matchMode)).add( - Restrictions.like("name.familyName2", name, matchMode)); - + + private Predicate getPredicateForLongName(CriteriaBuilder cb, Join nameJoin, String name, boolean includeVoided) { + String pattern = getMatchMode().toCaseSensitivePattern(name); + + Predicate givenNamePredicate = cb.like(nameJoin.get("givenName"), pattern); + Predicate middleNamePredicate = cb.like(nameJoin.get("middleName"), pattern); + Predicate familyNamePredicate = cb.like(nameJoin.get("familyName"), pattern); + Predicate familyName2Predicate = cb.like(nameJoin.get("familyName2"), pattern); + + Predicate namePredicate = cb.or(givenNamePredicate, middleNamePredicate, familyNamePredicate, familyName2Predicate); + if (!includeVoided) { - return Restrictions.conjunction().add(Restrictions.eq("name.voided", false)).add(criterion); + Predicate nonVoidedPredicate = cb.isFalse(nameJoin.get("voided")); + namePredicate = cb.and(namePredicate, nonVoidedPredicate); } - return criterion; + + return namePredicate; } - - private Criterion getCriterionForNoExactName(String name, boolean includeVoided) { - MatchMode matchMode = getMatchMode(); - - Criterion criterion = Restrictions.conjunction().add( - Restrictions.disjunction().add( - Restrictions.conjunction().add(Restrictions.isNotNull("name.givenName")).add( - Restrictions.like("name.givenName", name, matchMode))).add( - Restrictions.conjunction().add(Restrictions.isNotNull("name.middleName")).add( - Restrictions.like("name.middleName", name, matchMode))).add( - Restrictions.conjunction().add(Restrictions.isNotNull("name.familyName")).add( - Restrictions.like("name.familyName", name, matchMode))).add( - Restrictions.conjunction().add(Restrictions.isNotNull("name.familyName2")).add( - Restrictions.like("name.familyName2", name, matchMode)))).add( - Restrictions.disjunction().add(Restrictions.isNull("name.givenName")).add( - Restrictions.ne("name.givenName", name))).add( - Restrictions.disjunction().add(Restrictions.isNull("name.middleName")).add( - Restrictions.ne("name.middleName", name))).add( - Restrictions.disjunction().add(Restrictions.isNull("name.familyName")).add( - Restrictions.ne("name.familyName", name))).add( - Restrictions.disjunction().add(Restrictions.isNull("name.familyName2")).add( - Restrictions.ne("name.familyName2", name))); - + + private Predicate getPredicateForNoExactName(CriteriaBuilder cb, Join nameJoin, String name, boolean includeVoided) { + String pattern = getMatchMode().toCaseSensitivePattern(name); + Predicate givenNamePredicate = cb.and( + cb.isNotNull(nameJoin.get("givenName")), + cb.like(nameJoin.get("givenName"), pattern) + ); + Predicate middleNamePredicate = cb.and( + cb.isNotNull(nameJoin.get("middleName")), + cb.like(nameJoin.get("middleName"), pattern) + ); + Predicate familyNamePredicate = cb.and( + cb.isNotNull(nameJoin.get("familyName")), + cb.like(nameJoin.get("familyName"), pattern) + ); + Predicate familyName2Predicate = cb.and( + cb.isNotNull(nameJoin.get("familyName2")), + cb.like(nameJoin.get("familyName2"), pattern) + ); + + Predicate namePredicates = cb.or(givenNamePredicate, middleNamePredicate, familyNamePredicate, familyName2Predicate); + + Predicate notGivenName = cb.or(cb.isNull(nameJoin.get("givenName")), cb.notEqual(nameJoin.get("givenName"), name)); + Predicate notMiddleName = cb.or(cb.isNull(nameJoin.get("middleName")), cb.notEqual(nameJoin.get("middleName"), name)); + Predicate notFamilyName = cb.or(cb.isNull(nameJoin.get("familyName")), cb.notEqual(nameJoin.get("familyName"), name)); + Predicate notFamilyName2 = cb.or(cb.isNull(nameJoin.get("familyName2")), cb.notEqual(nameJoin.get("familyName2"), name)); + + Predicate combinedPredicate = cb.and(namePredicates, notGivenName, notMiddleName, notFamilyName, notFamilyName2); + if (!includeVoided) { - return Restrictions.conjunction().add(Restrictions.eq("name.voided", false)).add(criterion); + combinedPredicate = cb.and(combinedPredicate, cb.isFalse(nameJoin.get("voided"))); } - return criterion; + + return combinedPredicate; } - + + /** * Should return start as default match mode * Should return start as configured match mode @@ -568,15 +629,15 @@ private Criterion getCriterionForNoExactName(String name, boolean includeVoided) */ MatchMode getMatchMode() { String matchMode = Context.getAdministrationService().getGlobalProperty( - OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_SEARCH_MATCH_MODE, - OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_SEARCH_MATCH_START); - + OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_SEARCH_MATCH_MODE, + OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_SEARCH_MATCH_START); + if (matchMode.equalsIgnoreCase(OpenmrsConstants.GLOBAL_PROPERTY_PATIENT_SEARCH_MATCH_ANYWHERE)) { return MatchMode.ANYWHERE; } return MatchMode.START; } - + /** * Puts @SEARCH@, @SEARCH-1@, and @CHECKDIGIT@ into the search string * @@ -588,28 +649,28 @@ private String replaceSearchString(String regex, String identifierSearched) { String returnString = regex.replaceAll("@SEARCH@", identifierSearched); if (identifierSearched.length() > 1) { // for 2 or more character searches, we allow regex to use last character as check digit - returnString = returnString.replaceAll("@SEARCH-1@", identifierSearched.substring(0, - identifierSearched.length() - 1)); - returnString = returnString.replaceAll("@CHECKDIGIT@", identifierSearched - .substring(identifierSearched.length() - 1)); + returnString = returnString.replaceAll("@SEARCH-1@", + identifierSearched.substring(0, identifierSearched.length() - 1)); + returnString = returnString.replaceAll("@CHECKDIGIT@", + identifierSearched.substring(identifierSearched.length() - 1)); } else { returnString = returnString.replaceAll("@SEARCH-1@", ""); returnString = returnString.replaceAll("@CHECKDIGIT@", ""); } return returnString; } - - private Criterion prepareCriterionForAttribute(String query, boolean includeVoided) { + + private Predicate prepareCriterionForAttribute(CriteriaBuilder cb, Join attributeJoin, Join attributeTypeJoin, String query, boolean includeVoided) { query = HibernateUtil.escapeSqlWildcards(query, sessionFactory); - - Conjunction conjunction = Restrictions.conjunction(); + MatchMode matchMode = personSearchCriteria.getAttributeMatchMode(); + List predicates = new ArrayList<>(); String[] queryParts = getQueryParts(query); for (String queryPart : queryParts) { - conjunction.add(personSearchCriteria.prepareCriterionForAttribute(queryPart, includeVoided, matchMode)); + predicates.add(personSearchCriteria.preparePredicateForAttribute(cb, attributeJoin, attributeTypeJoin, queryPart, includeVoided, matchMode)); } - - return conjunction; + + return cb.and(predicates.toArray(new Predicate[]{})); } } diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/PersonSearchCriteria.java b/api/src/main/java/org/openmrs/api/db/hibernate/PersonSearchCriteria.java index 4ce512543a92..80a657585141 100644 --- a/api/src/main/java/org/openmrs/api/db/hibernate/PersonSearchCriteria.java +++ b/api/src/main/java/org/openmrs/api/db/hibernate/PersonSearchCriteria.java @@ -9,60 +9,47 @@ */ package org.openmrs.api.db.hibernate; -import org.hibernate.Criteria; -import org.hibernate.criterion.Criterion; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Restrictions; -import org.hibernate.sql.JoinType; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Predicate; + +import java.util.ArrayList; +import java.util.List; + +import org.openmrs.Encounter; +import org.openmrs.Patient; import org.openmrs.api.AdministrationService; import org.openmrs.api.context.Context; +import org.openmrs.attribute.Attribute; +import org.openmrs.attribute.AttributeType; import org.openmrs.util.OpenmrsConstants; public class PersonSearchCriteria { - - Criterion prepareCriterionForAttribute(String value, MatchMode matchMode) { - return (prepareCriterionForAttribute(value, null, matchMode)); - } - - Criterion prepareCriterionForName(String value) { - return prepareCriterionForName(value, null); - } - - Criterion prepareCriterionForAttribute(String value, Boolean voided, MatchMode matchMode) { - if (voided == null || !voided) { - return Restrictions.conjunction().add(Restrictions.eq("attributeType.searchable", true)).add( - Restrictions.eq("attribute.voided", false)).add(Restrictions.ilike("attribute.value", value, matchMode)); - } else { - return Restrictions.conjunction().add(Restrictions.eq("attributeType.searchable", true)).add( - Restrictions.ilike("attribute.value", value, matchMode)); - } - } - - Criterion prepareCriterionForName(String value, Boolean voided) { + + Predicate preparePredicateForAttribute(CriteriaBuilder cb, Join attributeJoin, + Join attributeTypeJoin, String value, Boolean voided, MatchMode matchMode) { + List predicates = new ArrayList<>(); + predicates.add(cb.isTrue(attributeTypeJoin.get("searchable"))); + + predicates.add(cb.like(cb.lower(attributeJoin.get("value")), matchMode.toLowerCasePattern(value))); + if (voided == null || !voided) { - return Restrictions.conjunction().add(Restrictions.eq("name.voided", false)).add( - Restrictions.disjunction().add(Restrictions.ilike("name.givenName", value, MatchMode.START)).add( - Restrictions.ilike("name.middleName", value, MatchMode.START)).add( - Restrictions.ilike("name.familyName", value, MatchMode.START)).add( - Restrictions.ilike("name.familyName2", value, MatchMode.START))); - } else { - return Restrictions.conjunction().add( - Restrictions.disjunction().add(Restrictions.ilike("name.givenName", value, MatchMode.START)).add( - Restrictions.ilike("name.middleName", value, MatchMode.START)).add( - Restrictions.ilike("name.familyName", value, MatchMode.START)).add( - Restrictions.ilike("name.familyName2", value, MatchMode.START))); + predicates.add(cb.isFalse(attributeJoin.get("voided"))); } + + return cb.and(predicates.toArray(new Predicate[]{})); } - void addAliasForName(Criteria criteria) { - criteria.createAlias("names", "name"); + Join addAliasForAttribute(Join patientJoin) { + return patientJoin.join("attributes", JoinType.LEFT); } - - void addAliasForAttribute(Criteria criteria) { - criteria.createAlias("attributes", "attribute", JoinType.LEFT_OUTER_JOIN); - criteria.createAlias("attribute.attributeType", "attributeType", JoinType.LEFT_OUTER_JOIN); + + + Join addAliasForAttributeType(Join attributeJoin) { + return attributeJoin.join("attributeType", JoinType.LEFT); } - + MatchMode getAttributeMatchMode() { AdministrationService adminService = Context.getAdministrationService(); String matchModeProperty = adminService.getGlobalProperty( diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/QueryResult.java b/api/src/main/java/org/openmrs/api/db/hibernate/QueryResult.java new file mode 100644 index 000000000000..8267c2407787 --- /dev/null +++ b/api/src/main/java/org/openmrs/api/db/hibernate/QueryResult.java @@ -0,0 +1,54 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.api.db.hibernate; + +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; +import java.util.ArrayList; +import java.util.List; + +public class QueryResult { + private List predicates; + private List orders; + + public QueryResult() { + this.predicates = new ArrayList<>(); + this.orders = new ArrayList<>(); + } + + public QueryResult(List predicates, List orders) { + this.predicates = predicates; + this.orders = orders; + } + + public List getPredicates() { + return predicates; + } + + public void setPredicates(List predicates) { + this.predicates = predicates; + } + + public void addPredicates(List predicates) { + this.predicates.addAll(predicates); + } + public List getOrders() { + return orders; + } + + public void setOrders(List orders) { + this.orders = orders; + } + + public void addOrder(Order order) { + this.orders.add(order); + } +} + diff --git a/api/src/main/java/org/openmrs/api/db/hibernate/search/CriteriaQuery.java b/api/src/main/java/org/openmrs/api/db/hibernate/search/CriteriaQuery.java deleted file mode 100644 index 857f71e1c009..000000000000 --- a/api/src/main/java/org/openmrs/api/db/hibernate/search/CriteriaQuery.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public License, - * v. 2.0. If a copy of the MPL was not distributed with this file, You can - * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under - * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. - * - * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS - * graphic logo is a trademark of OpenMRS Inc. - */ -package org.openmrs.api.db.hibernate.search; - -import java.util.List; - -import org.hibernate.Criteria; -import org.hibernate.HibernateException; -import org.hibernate.Session; -import org.hibernate.criterion.Projections; -import org.openmrs.collection.ListPart; - -/** - * Performs criteria queries. - * - * @since 1.11 - */ -public abstract class CriteriaQuery extends SearchQuery { - - private final Criteria criteria; - - /** - * @param session - */ - public CriteriaQuery(Session session, Class type) { - super(session, type); - criteria = getSession().createCriteria(getType()); - prepareCriteria(criteria); - } - - public abstract void prepareCriteria(Criteria criteria); - - @Override - public List list() { - criteria.setProjection(null); - criteria.setResultTransformer(Criteria.ROOT_ENTITY); - - @SuppressWarnings("unchecked") - List list = criteria.list(); - - return list; - } - - @Override - public ListPart listPart(Long firstResult, Long maxResults) { - criteria.setProjection(null); - criteria.setResultTransformer(Criteria.ROOT_ENTITY); - - if (firstResult != null) { - criteria.setFirstResult(firstResult.intValue()); - } - - if (maxResults != null) { - criteria.setMaxResults(maxResults.intValue()); - } - - @SuppressWarnings("unchecked") - List list = criteria.list(); - - return ListPart.newListPart(list, firstResult, maxResults, null, null); - } - - @Override - public T uniqueResult() throws HibernateException { - @SuppressWarnings("unchecked") - T result = (T) criteria.uniqueResult(); - - return result; - } - - /** - * @see org.openmrs.api.db.hibernate.search.SearchQuery#resultSize() - */ - @Override - public long resultSize() { - criteria.setProjection(Projections.rowCount()); - - return ((Number) criteria.uniqueResult()).longValue(); - } - -} diff --git a/api/src/main/java/org/openmrs/api/impl/AdministrationServiceImpl.java b/api/src/main/java/org/openmrs/api/impl/AdministrationServiceImpl.java index af47f7fbd537..91f9b5180b40 100644 --- a/api/src/main/java/org/openmrs/api/impl/AdministrationServiceImpl.java +++ b/api/src/main/java/org/openmrs/api/impl/AdministrationServiceImpl.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -33,6 +34,7 @@ import org.openmrs.GlobalProperty; import org.openmrs.ImplementationId; import org.openmrs.OpenmrsObject; +import org.openmrs.Privilege; import org.openmrs.User; import org.openmrs.api.APIException; import org.openmrs.api.AdministrationService; @@ -150,7 +152,54 @@ public String getGlobalProperty(String propertyName) throws APIException { return null; } - return dao.getGlobalProperty(propertyName); + GlobalProperty gp = dao.getGlobalPropertyObject(propertyName); + if (gp != null) { + if (canViewGlobalProperty(gp)) { + return gp.getPropertyValue(); + } else { + throw new APIException("GlobalProperty.error.privilege.required.view", new Object[] { + gp.getViewPrivilege().getPrivilege(), propertyName }); + } + } else { + return null; + } + } + + private boolean canViewGlobalProperty(GlobalProperty property) { + if (property.getViewPrivilege() == null) { + return true; + } + + return Context.getAuthenticatedUser().hasPrivilege(property.getViewPrivilege().getPrivilege()); + } + + private boolean canDeleteGlobalProperty(GlobalProperty property) { + if (property.getDeletePrivilege() == null) { + return true; + } + + return Context.getAuthenticatedUser().hasPrivilege(property.getDeletePrivilege().getPrivilege()); + } + + private boolean canEditGlobalProperty(GlobalProperty property) { + if (property.getEditPrivilege() == null) { + return true; + } + + return Context.getAuthenticatedUser().hasPrivilege(property.getEditPrivilege().getPrivilege()); + } + + private List filterGlobalPropertiesByViewPrivilege(List properties) { + if (properties != null) { + for (Iterator iterator = properties.iterator(); iterator.hasNext();) { + GlobalProperty property = iterator.next(); + Privilege vp = property.getViewPrivilege(); + if (vp != null && !Context.getAuthenticatedUser().hasPrivilege(vp.getPrivilege())) { + iterator.remove(); + } + } + } + return properties; } /** @@ -173,7 +222,17 @@ public String getGlobalProperty(String propertyName, String defaultValue) throws @Override @Transactional(readOnly = true) public GlobalProperty getGlobalPropertyObject(String propertyName) { - return dao.getGlobalPropertyObject(propertyName); + GlobalProperty gp = dao.getGlobalPropertyObject(propertyName); + if (gp != null) { + if (canViewGlobalProperty(gp)) { + return gp; + } else { + throw new APIException("GlobalProperty.error.privilege.required.view", new Object[] { + gp.getViewPrivilege().getPrivilege(), propertyName }); + } + } else { + return null; + } } /** @@ -201,6 +260,12 @@ public void updateGlobalProperty(String propertyName, String propertyValue) thro if (gp == null) { throw new IllegalStateException("Global property with the given propertyName does not exist" + propertyName); } + + if (!canEditGlobalProperty(gp)) { + throw new APIException("GlobalProperty.error.privilege.required.edit", new Object[] { + gp.getEditPrivilege().getPrivilege(), propertyName }); + } + gp.setPropertyValue(propertyValue); dao.saveGlobalProperty(gp); } @@ -211,7 +276,7 @@ public void updateGlobalProperty(String propertyName, String propertyValue) thro @Override @Transactional(readOnly = true) public List getAllGlobalProperties() throws APIException { - return dao.getAllGlobalProperties(); + return filterGlobalPropertiesByViewPrivilege(dao.getAllGlobalProperties()); } /** @@ -220,7 +285,7 @@ public List getAllGlobalProperties() throws APIException { @Override @Transactional(readOnly = true) public List getGlobalPropertiesByPrefix(String prefix) { - return dao.getGlobalPropertiesByPrefix(prefix); + return filterGlobalPropertiesByViewPrivilege(dao.getGlobalPropertiesByPrefix(prefix)); } /** @@ -229,7 +294,7 @@ public List getGlobalPropertiesByPrefix(String prefix) { @Override @Transactional(readOnly = true) public List getGlobalPropertiesBySuffix(String suffix) { - return dao.getGlobalPropertiesBySuffix(suffix); + return filterGlobalPropertiesByViewPrivilege(dao.getGlobalPropertiesBySuffix(suffix)); } /** @@ -237,6 +302,11 @@ public List getGlobalPropertiesBySuffix(String suffix) { */ @Override public void purgeGlobalProperty(GlobalProperty globalProperty) throws APIException { + if (!canDeleteGlobalProperty(globalProperty)) { + throw new APIException("GlobalProperty.error.privilege.required.purge", new Object[] { + globalProperty.getDeletePrivilege().getPrivilege(), globalProperty.getProperty() }); + } + notifyGlobalPropertyDelete(globalProperty.getProperty()); dao.deleteGlobalProperty(globalProperty); } @@ -265,6 +335,12 @@ public List saveGlobalProperties(List props) thr @Override @CacheEvict(value = "userSearchLocales", allEntries = true) public GlobalProperty saveGlobalProperty(GlobalProperty gp) throws APIException { + + if (!canEditGlobalProperty(gp)) { + throw new APIException("GlobalProperty.error.privilege.required.edit", new Object[] { + gp.getEditPrivilege().getPrivilege(), gp.getProperty() }); + } + // only try to save it if the global property has a key if (gp.getProperty() != null && gp.getProperty().length() > 0) { if (gp.getProperty().equals(OpenmrsConstants.GLOBAL_PROPERTY_LOCALE_ALLOWED_LIST)) { @@ -621,7 +697,15 @@ public boolean supportsPropertyName(String propertyName) { @Override @Transactional(readOnly = true) public GlobalProperty getGlobalPropertyByUuid(String uuid) { - return dao.getGlobalPropertyByUuid(uuid); + GlobalProperty gp = dao.getGlobalPropertyByUuid(uuid); + if (gp == null) { + return null; + } else if (canViewGlobalProperty(gp)) { + return gp; + } else { + throw new APIException("GlobalProperty.error.privilege.required.view", new Object[] { + gp.getViewPrivilege().getPrivilege(), gp.getProperty() }); + } } /** diff --git a/api/src/main/java/org/openmrs/api/impl/ConditionServiceImpl.java b/api/src/main/java/org/openmrs/api/impl/ConditionServiceImpl.java index 14f5deb70315..88430d327fd1 100644 --- a/api/src/main/java/org/openmrs/api/impl/ConditionServiceImpl.java +++ b/api/src/main/java/org/openmrs/api/impl/ConditionServiceImpl.java @@ -9,10 +9,12 @@ */ package org.openmrs.api.impl; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.openmrs.Condition; import org.openmrs.Encounter; import org.openmrs.Patient; +import org.openmrs.User; import org.openmrs.api.APIException; import org.openmrs.api.ConditionService; import org.openmrs.api.context.Context; @@ -101,46 +103,41 @@ public List getConditionsByEncounter(Encounter encounter) throws APIE */ @Override public Condition saveCondition(Condition condition) throws APIException { - Condition existingCondition = Context.getConditionService().getConditionByUuid(condition.getUuid()); + // If there is no existing condition, then we are creating a condition - if (existingCondition == null) { + Integer existingConditionId = condition.getConditionId(); + if (existingConditionId == null) { return conditionDAO.saveCondition(condition); } - - // If the incoming condition has been voided, we simply void the existing condition - // All other changes are ignored - if (condition.getVoided()) { - if (!existingCondition.getVoided()) { - return Context.getConditionService().voidCondition(existingCondition, - StringUtils.isNotBlank(condition.getVoidReason()) ? condition.getVoidReason() : "Condition deleted"); - } else { - return existingCondition; - } + + // If there is an existing condition, create a new condition from it and reset the existing instance state + Condition newCondition = Condition.newInstance(condition); + Context.refreshEntity(condition); + + // Determine how the existing state and new state have changed + boolean conditionHasChanged = !newCondition.matches(condition); + boolean existingVoided = BooleanUtils.isTrue(condition.getVoided()); + boolean newVoided = BooleanUtils.isTrue(newCondition.getVoided()); + boolean voidOriginal = !existingVoided && conditionHasChanged; + boolean saveNew = !newVoided && conditionHasChanged; + + // If the intention is to void or change the original Condition, then void the existing and save the new + if (voidOriginal) { + User currentUser = Context.getAuthenticatedUser(); + String reason = saveNew ? "Condition replaced by " + newCondition.getUuid() : "Condition removed"; + condition.setVoided(true); + condition.setVoidedBy(newCondition.getVoidedBy() == null ? currentUser : newCondition.getVoidedBy()); + condition.setVoidReason(newCondition.getVoidReason() == null ? reason : newCondition.getVoidReason()); + condition = conditionDAO.saveCondition(condition); } - // If the existing condition is voided, we will only calls to unvoid the condition - // All other changes are ignored - if (existingCondition.getVoided()) { - if (!condition.getVoided()) { - return Context.getConditionService().unvoidCondition(existingCondition); - } else { - return existingCondition; - } + if (saveNew) { + newCondition.setPreviousVersion(condition); + return conditionDAO.saveCondition(newCondition); } - - // If we got here, the updated condition and the existing condition are both live, so the updated condition is now - // replacing the existing condition - Condition newCondition = Condition.newInstance(condition); - newCondition.setPreviousVersion(existingCondition); - - if (!existingCondition.getVoided()) { - existingCondition.setVoided(true); - existingCondition.setVoidedBy(Context.getAuthenticatedUser()); - existingCondition.setVoidReason("Condition replaced"); - conditionDAO.saveCondition(existingCondition); + else { + return condition; } - - return conditionDAO.saveCondition(newCondition); } /** diff --git a/api/src/main/java/org/openmrs/api/impl/EncounterServiceImpl.java b/api/src/main/java/org/openmrs/api/impl/EncounterServiceImpl.java index 77a5d3e33b42..89cfabc5ba47 100644 --- a/api/src/main/java/org/openmrs/api/impl/EncounterServiceImpl.java +++ b/api/src/main/java/org/openmrs/api/impl/EncounterServiceImpl.java @@ -35,6 +35,7 @@ import org.openmrs.Visit; import org.openmrs.VisitType; import org.openmrs.api.APIException; +import org.openmrs.api.DiagnosisService; import org.openmrs.api.EncounterService; import org.openmrs.api.EncounterTypeLockedException; import org.openmrs.api.ObsService; @@ -199,8 +200,16 @@ public Encounter saveEncounter(Encounter encounter) throws APIException { // save the conditions encounter.getConditions().forEach(Context.getConditionService()::saveCondition); - + + // save the allergies encounter.getAllergies().forEach(Context.getPatientService()::saveAllergy); + + // save the diagnoses + encounter.getDiagnoses().stream().forEach(diagnosis -> { + diagnosis.setPatient(p); + diagnosis.setEncounter(encounter); + }); + encounter.getDiagnoses().forEach(Context.getDiagnosisService()::save); return encounter; } diff --git a/api/src/main/java/org/openmrs/api/impl/OrderServiceImpl.java b/api/src/main/java/org/openmrs/api/impl/OrderServiceImpl.java index 0a10f19a332e..8561bad09636 100644 --- a/api/src/main/java/org/openmrs/api/impl/OrderServiceImpl.java +++ b/api/src/main/java/org/openmrs/api/impl/OrderServiceImpl.java @@ -114,6 +114,14 @@ public synchronized Order saveOrder(Order order, OrderContext orderContext) thro */ @Override public OrderGroup saveOrderGroup(OrderGroup orderGroup) throws APIException { + return saveOrderGroup(orderGroup, null); + } + + /** + * @see org.openmrs.api.OrderService#saveOrderGroup(org.openmrs.OrderGroup, org.openmrs.api.OrderContext) + */ + @Override + public OrderGroup saveOrderGroup(OrderGroup orderGroup, OrderContext orderContext) throws APIException { if (orderGroup.getId() == null) { // an OrderGroup requires an encounter, which has a patient, so it // is odd that OrderGroup has a patient field. There is no obvious @@ -126,13 +134,13 @@ public OrderGroup saveOrderGroup(OrderGroup orderGroup) throws APIException { for (Order order : orders) { if (order.getId() == null) { order.setEncounter(orderGroup.getEncounter()); - Context.getOrderService().saveOrder(order, null); + Context.getOrderService().saveOrder(order, orderContext); } } Set nestedGroups = orderGroup.getNestedOrderGroups(); if (nestedGroups != null) { for (OrderGroup nestedGroup : nestedGroups) { - Context.getOrderService().saveOrderGroup(nestedGroup); + Context.getOrderService().saveOrderGroup(nestedGroup, orderContext); } } return orderGroup; diff --git a/api/src/main/java/org/openmrs/api/impl/PatientServiceImpl.java b/api/src/main/java/org/openmrs/api/impl/PatientServiceImpl.java index fec6292136ed..af1f5d203875 100644 --- a/api/src/main/java/org/openmrs/api/impl/PatientServiceImpl.java +++ b/api/src/main/java/org/openmrs/api/impl/PatientServiceImpl.java @@ -1318,8 +1318,7 @@ public IdentifierValidator getIdentifierValidator(String pivClassName) { return getIdentifierValidator((Class) Context.loadClass(pivClassName)); } catch (ClassNotFoundException e) { - log.error("Could not find patient identifier validator " + pivClassName, e); - return getDefaultIdentifierValidator(); + throw new PatientIdentifierException("Could not find patient identifier validator " + pivClassName, e); } } diff --git a/api/src/main/java/org/openmrs/api/impl/ProviderServiceImpl.java b/api/src/main/java/org/openmrs/api/impl/ProviderServiceImpl.java index a4ef63fb56c3..084ebcff5ad2 100644 --- a/api/src/main/java/org/openmrs/api/impl/ProviderServiceImpl.java +++ b/api/src/main/java/org/openmrs/api/impl/ProviderServiceImpl.java @@ -213,6 +213,15 @@ public ProviderAttributeType getProviderAttributeTypeByUuid(String uuid) { return dao.getProviderAttributeTypeByUuid(uuid); } + /** + * @see org.openmrs.api.ProviderService#getProviderAttributeTypeByName(String) + */ + @Override + @Transactional(readOnly = true) + public ProviderAttributeType getProviderAttributeTypeByName(String name) { + return dao.getProviderAttributeTypeByName(name); + } + /** * @see org.openmrs.api.ProviderService#getProviderAttribute(java.lang.Integer) */ diff --git a/api/src/main/java/org/openmrs/api/impl/UserServiceImpl.java b/api/src/main/java/org/openmrs/api/impl/UserServiceImpl.java index 2892a145009f..202135a91c93 100644 --- a/api/src/main/java/org/openmrs/api/impl/UserServiceImpl.java +++ b/api/src/main/java/org/openmrs/api/impl/UserServiceImpl.java @@ -19,6 +19,7 @@ import org.openmrs.annotation.Logging; import org.openmrs.api.*; import org.openmrs.api.context.Context; +import org.openmrs.api.context.Daemon; import org.openmrs.api.db.DAOException; import org.openmrs.api.db.LoginCredential; import org.openmrs.api.db.UserDAO; @@ -52,7 +53,7 @@ /** * Default implementation of the user service. This class should not be used on its own. The current * OpenMRS implementation should be fetched from the Context - * + * * @see org.openmrs.api.UserService * @see org.openmrs.api.context.Context */ @@ -63,9 +64,11 @@ public class UserServiceImpl extends BaseOpenmrsService implements UserService { protected UserDAO dao; - private static final int MAX_VALID_TIME = 12*60*60*1000; //Period of 12 hours - private static final int MIN_VALID_TIME = 60*1000; //Period of 1 minute - private static final int DEFAULT_VALID_TIME = 10*60*1000; //Default time of 10 minute + private static final int MAX_VALID_TIME = 12 * 60 * 60 * 1000; //Period of 12 hours + + private static final int MIN_VALID_TIME = 60 * 1000; //Period of 1 minute + + private static final int DEFAULT_VALID_TIME = 10 * 60 * 1000; //Default time of 10 minute public UserServiceImpl() { } @@ -79,7 +82,7 @@ public void setUserDAO(UserDAO dao) { */ private int getValidTime() { String validTimeGp = Context.getAdministrationService() - .getGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_VALIDTIME); + .getGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_VALIDTIME); final int validTime = StringUtils.isBlank(validTimeGp) ? DEFAULT_VALID_TIME : Integer.parseInt(validTimeGp); //if valid time is less that a minute or greater than 12hrs reset valid time to 1 minutes else set it to the required time. return (validTime < MIN_VALID_TIME) || (validTime > MAX_VALID_TIME) ? DEFAULT_VALID_TIME : validTime; @@ -105,7 +108,7 @@ public User createUser(User user, String password) throws APIException { if (hasDuplicateUsername(user)) { throw new DAOException("Username " + user.getUsername() + " or system id " + user.getSystemId() - + " is already in use."); + + " is already in use."); } // TODO Check required fields for user!! @@ -169,7 +172,7 @@ public User saveUser(User user) throws APIException { if (hasDuplicateUsername(user)) { throw new DAOException("Username " + user.getUsername() + " or system id " + user.getSystemId() - + " is already in use."); + + " is already in use."); } return dao.saveUser(user, null); @@ -217,7 +220,7 @@ public User unretireUser(User user) throws APIException { public List getAllUsers() throws APIException { return dao.getAllUsers(); } - + /** * @see org.openmrs.api.UserService#getAllPrivileges() */ @@ -255,7 +258,7 @@ public void purgePrivilege(Privilege privilege) throws APIException { public Privilege savePrivilege(Privilege privilege) throws APIException { return dao.savePrivilege(privilege); } - + /** * @see org.openmrs.api.UserService#getAllRoles() */ @@ -314,10 +317,9 @@ public Role saveRole(Role role) throws APIException { * @see org.openmrs.api.UserService#changePassword(java.lang.String, java.lang.String) */ @Override - public void changePassword(String pw, String pw2) throws APIException { + public void changePassword(String oldPassword, String newPassword) throws APIException { User user = Context.getAuthenticatedUser(); - - changePassword(user, pw, pw2); + changePassword(user, oldPassword, newPassword); } /** @@ -338,7 +340,7 @@ public void changeQuestionAnswer(User u, String question, String answer) throws /** * @see org.openmrs.api.UserService#changeQuestionAnswer(java.lang.String, java.lang.String, - * java.lang.String) + * java.lang.String) */ @Override public void changeQuestionAnswer(String pw, String q, String a) { @@ -383,23 +385,23 @@ public List getUsers(String nameSearch, List roles, boolean includeV /** * Convenience method to check if the authenticated user has all privileges they are giving out - * + * * @param user user that has privileges */ private void checkPrivileges(User user) { List requiredPrivs = user.getAllRoles().stream().peek(this::checkSuperUserPrivilege) - .map(Role::getPrivileges).filter(Objects::nonNull).flatMap(Collection::stream) - .map(Privilege::getPrivilege).filter(p -> !Context.hasPrivilege(p)).sorted().collect(Collectors.toList()); + .map(Role::getPrivileges).filter(Objects::nonNull).flatMap(Collection::stream) + .map(Privilege::getPrivilege).filter(p -> !Context.hasPrivilege(p)).sorted().collect(Collectors.toList()); if (requiredPrivs.size() == 1) { throw new APIException("User.you.must.have.privilege", new Object[] { requiredPrivs.get(0) }); } else if (requiredPrivs.size() > 1) { throw new APIException("User.you.must.have.privileges", new Object[] { String.join(", ", requiredPrivs) }); - } + } } private void checkSuperUserPrivilege(Role r) { if (r.getRole().equals(RoleConstants.SUPERUSER) - && !Context.hasPrivilege(PrivilegeConstants.ASSIGN_SYSTEM_DEVELOPER_ROLE)) { + && !Context.hasPrivilege(PrivilegeConstants.ASSIGN_SYSTEM_DEVELOPER_ROLE)) { throw new APIException("User.you.must.have.role", new Object[] { RoleConstants.SUPERUSER }); } } @@ -457,7 +459,7 @@ public User removeUserProperty(User user, String key) { /** * Generates system ids based on the following algorithm scheme: user_id-check digit - * + * * @see org.openmrs.api.UserService#generateSystemId() */ @Override @@ -512,19 +514,19 @@ public void purgeUser(User user, boolean cascade) throws APIException { /** * Convenience method to check if the authenticated user has all privileges they are giving out * to the new role - * - * @param role + * + * @param role */ private void checkPrivileges(Role role) { Optional.ofNullable(role.getPrivileges()) - .map(p -> p.stream().filter(pr -> !Context.hasPrivilege(pr.getPrivilege())).map(Privilege::getPrivilege) - .distinct().collect(Collectors.joining(", "))) - .ifPresent(missing -> { - if (StringUtils.isNotBlank(missing)) { - throw new APIException("Role.you.must.have.privileges", new Object[] { missing }); - } - }); - } + .map(p -> p.stream().filter(pr -> !Context.hasPrivilege(pr.getPrivilege())).map(Privilege::getPrivilege) + .distinct().collect(Collectors.joining(", "))) + .ifPresent(missing -> { + if (StringUtils.isNotBlank(missing)) { + throw new APIException("Role.you.must.have.privileges", new Object[] { missing }); + } + }); + } /** * @see org.openmrs.api.UserService#getPrivilegeByUuid(java.lang.String) @@ -560,7 +562,7 @@ public User getUserByUuid(String uuid) throws APIException { @Transactional(readOnly = true) public Integer getCountOfUsers(String name, List roles, boolean includeRetired) { if (name != null) { - name = StringUtils.replace(name,", ", " "); + name = StringUtils.replace(name, ", ", " "); } // if the authenticated role is in the list of searched roles, then all @@ -579,22 +581,22 @@ public Integer getCountOfUsers(String name, List roles, boolean includeRet @Override @Transactional(readOnly = true) public List getUsers(String name, List roles, boolean includeRetired, Integer start, Integer length) - throws APIException { + throws APIException { if (name != null) { - name = StringUtils.replace(name,", ", " "); + name = StringUtils.replace(name, ", ", " "); } if (roles == null) { roles = new ArrayList<>(); } - + // if the authenticated role is in the list of searched roles, then all // persons should be searched Role authRole = getRole(RoleConstants.AUTHENTICATED); if (roles.contains(authRole)) { return dao.getUsers(name, new ArrayList<>(), includeRetired, start, length); } - + // add the requested roles and all child roles for consideration Set allRoles = new HashSet<>(); for (Role r : roles) { @@ -638,47 +640,54 @@ public void changePassword(User user, String oldPassword, String newPassword) th if (user.getUserId() == null) { throw new APIException("user.must.exist", (Object[]) null); } + if (oldPassword == null) { if (!Context.hasPrivilege(PrivilegeConstants.EDIT_USER_PASSWORDS)) { throw new APIException("null.old.password.privilege.required", (Object[]) null); } } else if (!dao.getLoginCredential(user).checkPassword(oldPassword)) { throw new APIException("old.password.not.correct", (Object[]) null); - - } else if (newPassword != null && oldPassword.equals(newPassword)) { + + } else if (oldPassword.equals(newPassword)) { throw new APIException("new.password.equal.to.old", (Object[]) null); } + + if ("admin".equals(user.getUsername()) && Boolean.parseBoolean( + Context.getRuntimeProperties().getProperty(ADMIN_PASSWORD_LOCKED_PROPERTY, "false"))) { + throw new APIException("admin.password.is.locked"); + } + updatePassword(user, newPassword); } - + + @Override + public void changePassword(User user, String newPassword) { + updatePassword(user, newPassword); + } + private void updatePassword(User user, String newPassword) { OpenmrsUtil.validatePassword(user.getUsername(), newPassword, user.getSystemId()); dao.changePassword(user, newPassword); } - - @Override - public void changePassword(User user, String newPassword) throws APIException { - updatePassword(user, newPassword); - } - + @Override public void changePasswordUsingSecretAnswer(String secretAnswer, String pw) throws APIException { User user = Context.getAuthenticatedUser(); - if(!isSecretAnswer(user, secretAnswer)) { + if (!isSecretAnswer(user, secretAnswer)) { throw new APIException("secret.answer.not.correct", (Object[]) null); } updatePassword(user, pw); } - + @Override - public String getSecretQuestion(User user) throws APIException { + public String getSecretQuestion(User user) throws APIException { if (user.getUserId() != null) { LoginCredential loginCredential = dao.getLoginCredential(user); return loginCredential.getSecretQuestion(); } else { return null; } - } + } /** * @see org.openmrs.api.UserService#getUserByUsernameOrEmail(java.lang.String) @@ -698,7 +707,6 @@ public User getUserByUsernameOrEmail(String usernameOrEmail) { /** * @see org.openmrs.api.UserService#getUserByActivationKey(java.lang.String) - * */ @Override @Transactional(readOnly = true) @@ -725,48 +733,48 @@ public User setUserActivationKey(User user) throws MessageException { String hashedKey = Security.encodeString(token); String activationKey = hashedKey + ":" + time; LoginCredential credentials = dao.getLoginCredential(user); - credentials.setActivationKey(activationKey); - dao.setUserActivationKey(credentials); + credentials.setActivationKey(activationKey); + dao.setUserActivationKey(credentials); MessageSourceService messages = Context.getMessageSourceService(); AdministrationService adminService = Context.getAdministrationService(); Locale locale = getDefaultLocaleForUser(user); -// Delete this method call when removing {@link OpenmrsConstants#GP_HOST_URL} + // Delete this method call when removing {@link OpenmrsConstants#GP_HOST_URL} copyHostURLGlobalPropertyToPasswordResetGlobalProperty(adminService); - + String link = adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_URL) - .replace("{activationKey}", token); + .replace("{activationKey}", token); Properties mailProperties = Context.getMailProperties(); String sender = mailProperties.getProperty("mail.from"); - String subject = messages.getMessage("mail.passwordreset.subject",null, locale); + String subject = messages.getMessage("mail.passwordreset.subject", null, locale); String msg = messages.getMessage("mail.passwordreset.content", null, locale) - .replace("{name}", user.getUsername()) - .replace("{link}", link) - .replace("{time}", String.valueOf(getValidTime() / 60000)); + .replace("{name}", user.getUsername()) + .replace("{link}", link) + .replace("{time}", String.valueOf(getValidTime() / 60000)); Context.getMessageService().sendMessage(user.getEmail(), sender, subject, msg); return user; } - + /** * Delete this method when deleting {@link OpenmrsConstants#GP_HOST_URL} */ private void copyHostURLGlobalPropertyToPasswordResetGlobalProperty(AdministrationService adminService) { String hostURLGP = adminService.getGlobalProperty(OpenmrsConstants.GP_HOST_URL); String passwordResetGP = adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_URL); - if(StringUtils.isNotBlank(hostURLGP) && StringUtils.isBlank(passwordResetGP)) { - adminService.setGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_URL, hostURLGP); + if (StringUtils.isNotBlank(hostURLGP) && StringUtils.isBlank(passwordResetGP)) { + adminService.setGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_URL, hostURLGP); } } - + /** - * @see UserService#getDefaultLocaleForUser(User) + * @see UserService#getDefaultLocaleForUser(User) */ @Override public Locale getDefaultLocaleForUser(User user) { @@ -777,7 +785,8 @@ public Locale getDefaultLocaleForUser(User user) { if (StringUtils.isNotBlank(preferredLocale)) { locale = LocaleUtility.fromSpecification(preferredLocale); } - } catch (Exception e) { + } + catch (Exception e) { log.warn("Unable to parse user locale into a Locale", e); } } @@ -796,6 +805,7 @@ public void changePasswordUsingActivationKey(String activationKey, String newPas if (user == null) { throw new InvalidActivationKeyException("activation.key.not.correct"); } + updatePassword(user, newPassword); } } diff --git a/api/src/main/java/org/openmrs/hl7/db/hibernate/HibernateHL7DAO.java b/api/src/main/java/org/openmrs/hl7/db/hibernate/HibernateHL7DAO.java index 3040737b1367..f3a99fa6f2a5 100644 --- a/api/src/main/java/org/openmrs/hl7/db/hibernate/HibernateHL7DAO.java +++ b/api/src/main/java/org/openmrs/hl7/db/hibernate/HibernateHL7DAO.java @@ -9,19 +9,24 @@ */ package org.openmrs.hl7.db.hibernate; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import org.hibernate.Criteria; -import org.hibernate.Query; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.MatchMode; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Restrictions; import org.hibernate.type.StandardBasicTypes; import org.openmrs.api.context.Context; import org.openmrs.api.db.DAOException; +import org.openmrs.api.db.hibernate.HibernateUtil; +import org.openmrs.api.db.hibernate.JpaUtils; +import org.openmrs.api.db.hibernate.MatchMode; import org.openmrs.hl7.HL7Constants; import org.openmrs.hl7.HL7InArchive; import org.openmrs.hl7.HL7InError; @@ -70,7 +75,7 @@ public HL7Source saveHL7Source(HL7Source hl7Source) throws DAOException { */ @Override public HL7Source getHL7Source(Integer hl7SourceId) throws DAOException { - return (HL7Source) sessionFactory.getCurrentSession().get(HL7Source.class, hl7SourceId); + return sessionFactory.getCurrentSession().get(HL7Source.class, hl7SourceId); } /** @@ -78,11 +83,16 @@ public HL7Source getHL7Source(Integer hl7SourceId) throws DAOException { */ @Override public HL7Source getHL7SourceByName(String name) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(HL7Source.class); - crit.add(Restrictions.eq("name", name)); - return (HL7Source) crit.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(HL7Source.class); + Root root = cq.from(HL7Source.class); + + cq.where(cb.equal(root.get("name"), name)); + + return session.createQuery(cq).uniqueResult(); } - + /** * @see org.openmrs.hl7.db.HL7DAO#getAllHL7Sources() */ @@ -114,13 +124,12 @@ public HL7InQueue saveHL7InQueue(HL7InQueue hl7InQueue) throws DAOException { */ @Override public HL7InQueue getHL7InQueue(Integer hl7InQueueId) throws DAOException { - return (HL7InQueue) sessionFactory.getCurrentSession().get(HL7InQueue.class, hl7InQueueId); + return sessionFactory.getCurrentSession().get(HL7InQueue.class, hl7InQueueId); } @Override public HL7InQueue getHL7InQueueByUuid(String uuid) throws DAOException { - return (HL7InQueue) sessionFactory.getCurrentSession().createCriteria(HL7InQueue.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, HL7InQueue.class, uuid); } /** @@ -142,30 +151,34 @@ public List getAllHL7InQueues() throws DAOException { * @return a Criteria object */ @SuppressWarnings("rawtypes") - private Criteria getHL7SearchCriteria(Class clazz, Integer messageState, String query) throws DAOException { + private List getHL7SearchCriteria(CriteriaBuilder cb, Root root, Class clazz, Integer messageState, String query) throws DAOException { if (clazz == null) { throw new DAOException("no class defined for HL7 search"); } - - Criteria crit = sessionFactory.getCurrentSession().createCriteria(clazz); - + + List predicates = new ArrayList<>(); + if (query != null && !query.isEmpty()) { + String pattern = MatchMode.ANYWHERE.toCaseSensitivePattern(query); + if (clazz == HL7InError.class) { - crit.add(Restrictions.or(Restrictions.like("HL7Data", query, MatchMode.ANYWHERE), Restrictions.or( - Restrictions.like("errorDetails", query, MatchMode.ANYWHERE), Restrictions.like("error", query, - MatchMode.ANYWHERE)))); + Predicate hl7DataPredicate = cb.like(root.get("HL7Data"), pattern); + Predicate errorDetailsPredicate = cb.like(root.get("errorDetails"), pattern); + Predicate errorPredicate = cb.like(root.get("error"), pattern); + + predicates.add(cb.or(hl7DataPredicate, errorDetailsPredicate, errorPredicate)); } else { - crit.add(Restrictions.like("HL7Data", query, MatchMode.ANYWHERE)); + predicates.add(cb.like(root.get("HL7Data"), pattern)); } } - + if (messageState != null) { - crit.add(Restrictions.eq("messageState", messageState)); + predicates.add(cb.equal(root.get("messageState"), messageState)); } - - return crit; + + return predicates; } - + /** * @see org.openmrs.hl7.db.HL7DAO#getHL7Batch(Class, int, int, Integer, String) */ @@ -173,11 +186,21 @@ private Criteria getHL7SearchCriteria(Class clazz, Integer messageState, String @SuppressWarnings( { "rawtypes", "unchecked" }) public List getHL7Batch(Class clazz, int start, int length, Integer messageState, String query) throws DAOException { - Criteria crit = getHL7SearchCriteria(clazz, messageState, query); - crit.setFirstResult(start); - crit.setMaxResults(length); - crit.addOrder(Order.asc("dateCreated")); - return crit.list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(clazz); + Root root = cq.from(clazz); + + List predicates = getHL7SearchCriteria(cb, root, clazz, messageState, query); + + cq.where(predicates.toArray(new Predicate[]{})) + .orderBy(cb.asc(root.get("dateCreated"))); + + TypedQuery typedQuery = session.createQuery(cq); + typedQuery.setFirstResult(start); + typedQuery.setMaxResults(length); + + return typedQuery.getResultList(); } /** @@ -186,9 +209,16 @@ public List getHL7Batch(Class clazz, int start, int length, Integer messa @Override @SuppressWarnings("rawtypes") public Long countHL7s(Class clazz, Integer messageState, String query) { - Criteria crit = getHL7SearchCriteria(clazz, messageState, query); - crit.setProjection(Projections.rowCount()); - return (Long) crit.uniqueResult(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + Root root = cq.from(clazz); + + List predicates = getHL7SearchCriteria(cb, root, clazz, messageState, query); + cq.select(cb.count(root)) + .where(predicates.toArray(new Predicate[]{})); + + return session.createQuery(cq).getSingleResult(); } /** @@ -202,7 +232,7 @@ public HL7InQueue getNextHL7InQueue() throws DAOException { if (query == null) { return null; } - return (HL7InQueue) query.uniqueResult(); + return JpaUtils.getSingleResultOrNull(query); } /** @@ -227,7 +257,7 @@ public HL7InArchive saveHL7InArchive(HL7InArchive hl7InArchive) throws DAOExcept */ @Override public HL7InArchive getHL7InArchive(Integer hl7InArchiveId) throws DAOException { - return (HL7InArchive) sessionFactory.getCurrentSession().get(HL7InArchive.class, hl7InArchiveId); + return sessionFactory.getCurrentSession().get(HL7InArchive.class, hl7InArchiveId); } /** @@ -248,7 +278,7 @@ private List getHL7InArchiveByState(Integer state, Integer maxResu if (maxResults != null) { q.setMaxResults(maxResults); } - return q.list(); + return q.getResultList(); } /** @@ -279,7 +309,7 @@ public List getAllHL7InArchives(Integer maxResults) { if (maxResults != null) { q.setMaxResults(maxResults); } - return q.list(); + return q.getResultList(); } /** @@ -304,13 +334,12 @@ public HL7InError saveHL7InError(HL7InError hl7InError) throws DAOException { */ @Override public HL7InError getHL7InError(Integer hl7InErrorId) throws DAOException { - return (HL7InError) sessionFactory.getCurrentSession().get(HL7InError.class, hl7InErrorId); + return sessionFactory.getCurrentSession().get(HL7InError.class, hl7InErrorId); } @Override public HL7InError getHL7InErrorByUuid(String uuid) throws DAOException { - return (HL7InError) sessionFactory.getCurrentSession().createCriteria(HL7InError.class).add( - Restrictions.eq("uuid", uuid)).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, HL7InError.class, uuid); } /** @@ -345,7 +374,7 @@ public void garbageCollect() { public HL7InArchive getHL7InArchiveByUuid(String uuid) throws DAOException { Query query = sessionFactory.getCurrentSession().createQuery("from HL7InArchive where uuid = ?0").setParameter(0, uuid, StandardBasicTypes.STRING); - Object record = query.uniqueResult(); + Object record = JpaUtils.getSingleResultOrNull(query); if (record == null) { return null; } @@ -356,17 +385,25 @@ public HL7InArchive getHL7InArchiveByUuid(String uuid) throws DAOException { * @see org.openmrs.hl7.db.HL7DAO#getHL7InArchivesToMigrate() */ @Override - @SuppressWarnings("unchecked") public List getHL7InArchivesToMigrate() { Integer daysToKeep = Hl7InArchivesMigrateThread.getDaysKept(); - Criteria crit = getHL7SearchCriteria(HL7InArchive.class, HL7Constants.HL7_STATUS_PROCESSED, null); - crit.setMaxResults(HL7Constants.MIGRATION_MAX_BATCH_SIZE); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(HL7InArchive.class); + Root root = cq.from(HL7InArchive.class); + + List predicates = getHL7SearchCriteria(cb, root, HL7InArchive.class, HL7Constants.HL7_STATUS_PROCESSED, null); + if (daysToKeep != null) { Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -1 * daysToKeep); - crit.add(Restrictions.lt("dateCreated", cal.getTime())); + predicates.add(cb.lessThan(root.get("dateCreated"), cal.getTime())); } - return crit.list(); + + cq.where(predicates.toArray(new Predicate[]{})); + return session.createQuery(cq) + .setMaxResults(HL7Constants.MIGRATION_MAX_BATCH_SIZE) + .getResultList(); } } diff --git a/api/src/main/java/org/openmrs/layout/name/NameSupport.java b/api/src/main/java/org/openmrs/layout/name/NameSupport.java index d6454caaf8f4..4571407aeaf2 100644 --- a/api/src/main/java/org/openmrs/layout/name/NameSupport.java +++ b/api/src/main/java/org/openmrs/layout/name/NameSupport.java @@ -9,16 +9,29 @@ */ package org.openmrs.layout.name; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.openmrs.GlobalProperty; import org.openmrs.api.APIException; +import org.openmrs.api.GlobalPropertyListener; import org.openmrs.api.context.Context; import org.openmrs.layout.LayoutSupport; +import org.openmrs.util.OpenmrsConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @since 1.12 */ -public class NameSupport extends LayoutSupport { - +public class NameSupport extends LayoutSupport implements GlobalPropertyListener { + + private static final Logger log = LoggerFactory.getLogger(NameSupport.class); private static NameSupport singleton; + private boolean initialized = false; public NameSupport() { if (singleton == null) { @@ -30,13 +43,98 @@ public static NameSupport getInstance() { if (singleton == null) { throw new APIException("Not Yet Instantiated"); } else { + singleton.init(); return singleton; } } + + /** + * Initializes layout templates with a custom template configured + * via the "layout.name.template" GP. + */ + private void init() { + if (initialized) { + return; + } + Context.getAdministrationService().addGlobalPropertyListener(singleton); + // Get configured name template to override the existing one if any + String layoutTemplateXml = Context.getAdministrationService().getGlobalProperty( + OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_TEMPLATE); + NameTemplate nameTemplate = deserializeXmlTemplate(layoutTemplateXml); + + if (nameTemplate != null) { + updateLayoutTemplates(nameTemplate); + initialized = true; + } + } + + /** + * Update existing layout templates if present with the provided template + */ + private void updateLayoutTemplates(NameTemplate nameTemplate) { + if (getLayoutTemplates() == null) { + setLayoutTemplates(new ArrayList<>()); + } + List list = new ArrayList<>(); + // filter out unaffected templates to keep + list.addAll(getLayoutTemplates().stream().filter(existingTemplate -> existingTemplate.getCodeName() != nameTemplate.getCodeName()).collect(Collectors.toList())); + list.add(nameTemplate); + setLayoutTemplates(list); + } + /** + * @return Returns the defaultLayoutFormat + */ + private NameTemplate deserializeXmlTemplate(String xml) { + NameTemplate nameTemplate = null; + if (StringUtils.isBlank(xml)) { + return null; + } + try { + nameTemplate = Context.getSerializationService().getDefaultSerializer().deserialize(xml, + NameTemplate.class); + } catch (Exception e) { + log.error("Error in deserializing provided name template", e); + } + return nameTemplate; + } + + /** + * @return Returns the defaultLayoutFormat + */ @Override public String getDefaultLayoutFormat() { - String ret = Context.getAdministrationService().getGlobalProperty("layout.name.format"); + String ret = Context.getAdministrationService().getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_FORMAT); return (ret != null && ret.length() > 0) ? ret : defaultLayoutFormat; } + + /** + * @see org.openmrs.api.GlobalPropertyListener#supportsPropertyName(String) + */ + @Override + public boolean supportsPropertyName(String propertyName) { + return OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_TEMPLATE.equals(propertyName); + } + + /** + * @see org.openmrs.api.GlobalPropertyListener#globalPropertyChanged(org.openmrs.GlobalProperty) + */ + @Override + public void globalPropertyChanged(GlobalProperty newValue) { + if (!OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_TEMPLATE.equals(newValue.getPropertyValue())) { + return; + } + NameTemplate nameTemplate = deserializeXmlTemplate(newValue.getPropertyValue()); + if (nameTemplate != null) { + updateLayoutTemplates(nameTemplate); + } + } + + /** + * @see org.openmrs.api.GlobalPropertyListener#globalPropertyDeleted(String) + */ + @Override + public void globalPropertyDeleted(String propertyName) { + + } } diff --git a/api/src/main/java/org/openmrs/liquibase/ChangeLogVersions.java b/api/src/main/java/org/openmrs/liquibase/ChangeLogVersions.java index f4e630d94dcc..37cc6693fc85 100644 --- a/api/src/main/java/org/openmrs/liquibase/ChangeLogVersions.java +++ b/api/src/main/java/org/openmrs/liquibase/ChangeLogVersions.java @@ -27,14 +27,14 @@ public class ChangeLogVersions { * openmrs-core/api/src/main/resources/liquibase/snapshots/schema-only. If the actual change log * files and this list get out of sync, org.openmrs.liquibase.ChangeLogVersionsTest fails. */ - private static final List SNAPSHOT_VERSIONS = Arrays.asList("1.9.x", "2.1.x", "2.2.x", "2.3.x", "2.4.x", "2.5.x"); + private static final List SNAPSHOT_VERSIONS = Arrays.asList("1.9.x", "2.1.x", "2.2.x", "2.3.x", "2.4.x", "2.5.x", "2.6.x"); /** * This definition of Liquibase update versions needs to be kept in sync with the actual change log * files in openmrs-core/api/src/main/resources/liquibase/updates. If the actual change log files * and this list get out of sync, org.openmrs.liquibase.ChangeLogVersionsTest fails. */ - private static final List UPDATE_VERSIONS = Arrays.asList("1.9.x", "2.0.x", "2.1.x", "2.2.x", "2.3.x", "2.4.x", "2.5.x", "2.6.x"); + private static final List UPDATE_VERSIONS = Arrays.asList("1.9.x", "2.0.x", "2.1.x", "2.2.x", "2.3.x", "2.4.x", "2.5.x", "2.6.x", "2.7.x"); public List getSnapshotVersions() { return SNAPSHOT_VERSIONS; diff --git a/api/src/main/java/org/openmrs/liquibase/OpenmrsClassLoaderResourceAccessor.java b/api/src/main/java/org/openmrs/liquibase/OpenmrsClassLoaderResourceAccessor.java new file mode 100644 index 000000000000..dd859362de9c --- /dev/null +++ b/api/src/main/java/org/openmrs/liquibase/OpenmrsClassLoaderResourceAccessor.java @@ -0,0 +1,47 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.liquibase; + +import liquibase.resource.ClassLoaderResourceAccessor; +import liquibase.resource.InputStreamList; +import org.openmrs.util.OpenmrsClassLoader; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +/** + * A customization of Liquibase's {@link ClassLoaderResourceAccessor} which defaults to the OpenMRS ClassLoader and has + * special handling for our liquibase.xml files, which occur multiple times on the classpath. + */ +public class OpenmrsClassLoaderResourceAccessor extends ClassLoaderResourceAccessor { + + public OpenmrsClassLoaderResourceAccessor() { + super(OpenmrsClassLoader.getInstance()); + } + + public OpenmrsClassLoaderResourceAccessor(ClassLoader classLoader) { + super(classLoader); + } + + @Override + public InputStreamList openStreams(String relativeTo, String streamPath) throws IOException { + InputStreamList result = super.openStreams(relativeTo, streamPath); + if (result != null && !result.isEmpty() && result.size() > 1) { + try (InputStreamList oldResult = result) { + URI uri = oldResult.getURIs().get(0); + result = new InputStreamList(uri, uri.toURL().openStream()); + } + + } + + return result; + } +} diff --git a/api/src/main/java/org/openmrs/logic/LogicCriteria.java b/api/src/main/java/org/openmrs/logic/LogicCriteria.java index a91b93c17b80..c1dac315925f 100644 --- a/api/src/main/java/org/openmrs/logic/LogicCriteria.java +++ b/api/src/main/java/org/openmrs/logic/LogicCriteria.java @@ -13,11 +13,11 @@ import java.util.Date; import java.util.Map; -import org.hibernate.criterion.Distinct; import org.openmrs.logic.op.And; import org.openmrs.logic.op.AsOf; import org.openmrs.logic.op.Average; import org.openmrs.logic.op.Count; +import org.openmrs.logic.op.Distinct; import org.openmrs.logic.op.First; import org.openmrs.logic.op.GreaterThan; import org.openmrs.logic.op.GreaterThanEquals; diff --git a/api/src/main/java/org/openmrs/module/ModuleFactory.java b/api/src/main/java/org/openmrs/module/ModuleFactory.java index 2c3eb325d56a..9f4d217f1f81 100644 --- a/api/src/main/java/org/openmrs/module/ModuleFactory.java +++ b/api/src/main/java/org/openmrs/module/ModuleFactory.java @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. - * + * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ @@ -14,7 +14,6 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; -import java.rmi.activation.Activator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -28,12 +27,15 @@ import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; -import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import org.aopalliance.aop.Advice; import org.openmrs.GlobalProperty; import org.openmrs.Privilege; +import org.openmrs.api.APIException; import org.openmrs.api.AdministrationService; import org.openmrs.api.OpenmrsService; import org.openmrs.api.context.Context; @@ -65,28 +67,31 @@ private ModuleFactory() { private static final Logger log = LoggerFactory.getLogger(ModuleFactory.class); - protected static volatile Map loadedModules = new WeakHashMap<>(); + protected static final Cache loadedModules = CacheBuilder.newBuilder() + .softValues().build(); - protected static volatile Map startedModules = new WeakHashMap<>(); + protected static final Cache startedModules = CacheBuilder.newBuilder() + .softValues().build(); - protected static volatile Map> extensionMap = new HashMap<>(); + protected static final Map> extensionMap = new HashMap<>(); // maps to keep track of the memory and objects to free/close - protected static volatile Map moduleClassLoaders = new WeakHashMap<>(); + protected static final Cache moduleClassLoaders = CacheBuilder.newBuilder().weakKeys() + .softValues().build(); - private static Map> providedPackages = new ConcurrentHashMap<>(); + private static final Map> providedPackages = new ConcurrentHashMap<>(); // the name of the file within a module file private static final String MODULE_CHANGELOG_FILENAME = "liquibase.xml"; - private static final Map daemonTokens = new WeakHashMap<>(); + private static final Cache daemonTokens = CacheBuilder.newBuilder().softValues().build(); - private static volatile Set actualStartupOrder; + private static final Set actualStartupOrder = new LinkedHashSet<>(); /** * Add a module (in the form of a jar file) to the list of openmrs modules Returns null if an error * occurred and/or module was not successfully loaded - * + * * @param moduleFile * @return Module */ @@ -99,7 +104,7 @@ public static Module loadModule(File moduleFile) throws ModuleException { /** * Add a module (in the form of a jar file) to the list of openmrs modules Returns null if an error * occurred and/or module was not successfully loaded - * + * * @param moduleFile * @param replaceIfExists unload a module that has the same moduleId if one is loaded already * @return Module @@ -116,7 +121,7 @@ public static Module loadModule(File moduleFile, Boolean replaceIfExists) throws /** * Add a module to the list of openmrs modules - * + * * @param module * @param replaceIfExists unload a module that has the same moduleId if one is loaded already * Should load module if it is currently not loaded @@ -176,7 +181,7 @@ public static void loadModules() { /** * Attempt to load the given files as OpenMRS modules - * + * * @param modulesToLoad the list of files to try and load Should not crash when * file is not found or broken Should setup requirement mappings for * every module Should not start the loaded modules @@ -272,7 +277,7 @@ public static void startModules() { /** * Obtain the list of modules that should be started - * + * * @return list of modules */ private static List getModulesThatShouldStart() { @@ -291,7 +296,7 @@ private static List getModulesThatShouldStart() { // if a 'moduleid.started' property doesn't exist, start the module anyway // as this is probably the first time they are loading it if (startedProp == null || "true".equals(startedProp) || "true".equalsIgnoreCase(mandatoryProp) - || mod.isMandatory() || isCoreToOpenmrs) { + || mod.isMandatory() || isCoreToOpenmrs) { modules.add(mod); } } @@ -300,7 +305,7 @@ private static List getModulesThatShouldStart() { /** * Sort modules in startup order based on required and aware-of dependencies - * + * * @param modules list of modules to sort * @return list of modules sorted by dependencies * @throws CycleException @@ -322,8 +327,8 @@ public static List getModulesInStartupOrder(Collection modules) if (fromNode != null) { graph.addEdge(graph.new Edge( - fromNode, - mod)); + fromNode, + mod)); } } @@ -337,8 +342,8 @@ public static List getModulesInStartupOrder(Collection modules) if (fromNode != null) { graph.addEdge(graph.new Edge( - fromNode, - mod)); + fromNode, + mod)); } } } @@ -348,7 +353,7 @@ public static List getModulesInStartupOrder(Collection modules) /** * Send an Alert to all super users that the given module did not start successfully. - * + * * @param mod The Module that failed */ private static void notifySuperUsersAboutModuleFailure(Module mod) { @@ -387,7 +392,7 @@ private static void notifySuperUsersAboutCyclicDependencies(Exception ex) { /** * Returns all modules found/loaded into the system (started and not started), with the core modules * at the start of that list - * + * * @return List<Module> of the modules loaded into the system, with the core * modules first. */ @@ -406,7 +411,7 @@ public static List getLoadedModulesCoreFirst() { * Convenience method to return a List of Strings containing a description of which modules the * passed module requires but which are not started. The returned description of each module is the * moduleId followed by the required version if one is specified - * + * * @param module the module to check required modules for * @return List<String> of module names + optional required versions: "org.openmrs.formentry * 1.8, org.rg.patientmatching" @@ -436,7 +441,7 @@ private static List getMissingRequiredModules(Module module) { /** * Returns all modules found/loaded into the system (started and not started) - * + * * @return Collection<Module> of the modules loaded into the system */ public static Collection getLoadedModules() { @@ -450,31 +455,22 @@ public static Collection getLoadedModules() { /** * Returns all modules found/loaded into the system (started and not started) in the form of a * map<ModuleId, Module> - * + * * @return map<ModuleId, Module> */ public static Map getLoadedModulesMap() { - if (loadedModules == null) { - loadedModules = new WeakHashMap<>(); - } - - return loadedModules; + return loadedModules.asMap(); } /** * Returns all modules found/loaded into the system (started and not started) in the form of a * map<PackageName, Module> - * + * * @return map<PackageName, Module> */ public static Map getLoadedModulesMapPackage() { - if (loadedModules == null) { - loadedModules = new WeakHashMap<>(); - return loadedModules; - } - - Map map = new WeakHashMap<>(); - for (Module loadedModule : loadedModules.values()) { + Map map = new HashMap<>(); + for (Module loadedModule : getLoadedModulesMap().values()) { map.put(loadedModule.getPackageName(), loadedModule); } return map; @@ -482,7 +478,7 @@ public static Map getLoadedModulesMapPackage() { /** * Returns the modules that have been successfully started - * + * * @return Collection<Module> of the started modules */ public static Collection getStartedModules() { @@ -508,15 +504,11 @@ public static List getStartedModulesInOrder() { /** * Returns the modules that have been successfully started in the form of a map<ModuleId, * Module> - * + * * @return Map<ModuleId, Module> */ public static Map getStartedModulesMap() { - if (startedModules == null) { - startedModules = new WeakHashMap<>(); - } - - return startedModules; + return startedModules.asMap(); } /** @@ -562,7 +554,7 @@ public static Module startModule(Module module) throws ModuleException { * Module's activator. This method is run in a new thread and is authenticated as the Daemon user. * If a non null application context is passed in, it gets refreshed to make the module's services * available - * + * * @param module Module to start * @param isOpenmrsStartup Specifies whether this module is being started at application startup or * not, this argument is ignored if a null application context is passed in @@ -572,7 +564,7 @@ public static Module startModule(Module module) throws ModuleException { * @see Daemon#startModule(Module, boolean, AbstractRefreshableApplicationContext) */ public static Module startModule(Module module, boolean isOpenmrsStartup, - AbstractRefreshableApplicationContext applicationContext) throws ModuleException { + AbstractRefreshableApplicationContext applicationContext) throws ModuleException { if (!requiredModulesStarted(module)) { int missingModules = 0; @@ -612,7 +604,7 @@ public static Module startModule(Module module, boolean isOpenmrsStartup, *
* Runs through extensionPoints and then calls {@link BaseModuleActivator#willStart()} on the * Module's activator. - * + * * @param module Module to start */ public static Module startModuleInternal(Module module) throws ModuleException { @@ -630,14 +622,14 @@ public static Module startModuleInternal(Module module) throws ModuleException { *
* If a non null application context is passed in, it gets refreshed to make the module's services * available - * + * * @param module Module to start * @param isOpenmrsStartup Specifies whether this module is being started at application startup or * not, this argument is ignored if a null application context is passed in * @param applicationContext the spring application context instance to refresh */ public static Module startModuleInternal(Module module, boolean isOpenmrsStartup, - AbstractRefreshableApplicationContext applicationContext) throws ModuleException { + AbstractRefreshableApplicationContext applicationContext) throws ModuleException { if (module != null) { String moduleId = module.getModuleId(); @@ -686,7 +678,7 @@ public static Module startModuleInternal(Module module, boolean isOpenmrsStartup // Get existing extensions, and append the ones from the new module List extensions = getExtensionMap().computeIfAbsent(moduleExtensionEntry.getKey(), - k -> new ArrayList<>()); + k -> new ArrayList<>()); for (Extension ext : sortedModuleExtensions) { log.debug("Adding to mapping ext: " + ext.getExtensionId() + " ext.class: " + ext.getClass()); extensions.add(ext); @@ -724,9 +716,7 @@ public static Module startModuleInternal(Module module, boolean isOpenmrsStartup // effectively mark this module as started successfully getStartedModulesMap().put(moduleId, module); - if (actualStartupOrder == null) { - actualStartupOrder = new LinkedHashSet<>(); - } + actualStartupOrder.add(moduleId); try { @@ -735,7 +725,7 @@ public static Module startModuleInternal(Module module, boolean isOpenmrsStartup // save the mandatory status saveGlobalProperty(moduleId + ".mandatory", String.valueOf(module.isMandatory()), - getGlobalPropertyMandatoryModuleDescription(moduleId)); + getGlobalPropertyMandatoryModuleDescription(moduleId)); } catch (Exception e) { // pass over errors because this doesn't really concern startup @@ -848,30 +838,30 @@ public static Set getModuleClassLoadersForPackage(String pack /** * Gets the error message of a module which fails to start. - * + * * @param module the module that has failed to start. * @return the message text. */ private static String getFailedToStartModuleMessage(Module module) { String[] params = { module.getName(), String.join(",", getMissingRequiredModules(module)) }; return Context.getMessageSourceService().getMessage("Module.error.moduleCannotBeStarted", params, - Context.getLocale()); + Context.getLocale()); } /** * Gets the error message of cyclic dependencies between modules - * + * * @return the message text. */ private static String getCyclicDependenciesMessage(String message) { return Context.getMessageSourceService().getMessage("Module.error.cyclicDependencies", new Object[] { message }, - Context.getLocale()); + Context.getLocale()); } /** * Loop over the given module's advice objects and load them into the Context This needs to be * called for all started modules after every restart of the Spring Application Context - * + * * @param module */ public static void loadAdvice(Module module) { @@ -898,7 +888,7 @@ public static void loadAdvice(Module module) { /** * Execute the given sql diff section for the given module - * + * * @param module the module being executed on * @param version the version of this sql diff * @param sql the actual sql statements to run (separated by semi colons) @@ -946,7 +936,7 @@ private static void runDiff(Module module, String version, String sql) { Context.addProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES); String description = "DO NOT MODIFY. Current database version number for the " + module.getModuleId() - + " module."; + + " module."; if (gp == null) { log.info("Global property " + key + " was not found. Creating one now."); @@ -972,7 +962,7 @@ private static void runDiff(Module module, String version, String sql) { /** * Execute all not run changeSets in liquibase.xml for the given module - * + * * @param module the module being executed on */ private static void runLiquibase(Module module) { @@ -1007,7 +997,7 @@ private static void runLiquibase(Module module) { /** * Runs through the advice and extension points and removes from api.
* Also calls mod.Activator.shutdown() - * + * * @param mod module to stop * @see ModuleFactory#stopModule(Module, boolean, boolean) */ @@ -1018,7 +1008,7 @@ public static void stopModule(Module mod) { /** * Runs through the advice and extension points and removes from api.
* Also calls mod.Activator.shutdown() - * + * * @param mod the module to stop * @param isShuttingDown true if this is called during the process of shutting down openmrs * @see #stopModule(Module, boolean, boolean) @@ -1033,8 +1023,8 @@ public static void stopModule(Module mod, boolean isShuttingDown) { * it is shutting down. When normally stopping a module, use {@link #stopModule(Module)} (or leave * value as false). This property controls whether the globalproperty is set for startup/shutdown. *
- * Also calls module's {@link Activator#shutdown()} - * + * Also calls module's {@link ModuleActivator#stopped()} + * * @param mod module to stop * @param skipOverStartedProperty true if we don't want to set <moduleid>.started to false * @param isFailedStartup true if this is being called as a cleanup because of a failed module @@ -1043,7 +1033,7 @@ public static void stopModule(Module mod, boolean isShuttingDown) { * never be null. */ public static List stopModule(Module mod, boolean skipOverStartedProperty, boolean isFailedStartup) - throws ModuleMustStartException { + throws ModuleMustStartException { List dependentModulesStopped = new ArrayList<>(); @@ -1082,7 +1072,7 @@ public static List stopModule(Module mod, boolean skipOverStartedPropert List startedModulesCopy = new ArrayList<>(getStartedModules()); for (Module dependentModule : startedModulesCopy) { if (dependentModule != null && !dependentModule.equals(mod) - && isModuleRequiredByAnother(dependentModule, modulePackage)) { + && isModuleRequiredByAnother(dependentModule, modulePackage)) { dependentModulesStopped.add(dependentModule); dependentModulesStopped.addAll(stopModule(dependentModule, skipOverStartedProperty, isFailedStartup)); } @@ -1112,7 +1102,7 @@ && isModuleRequiredByAnother(dependentModule, modulePackage)) { try { cls = Context.loadClass(advice.getPoint()); Object aopObject = advice.getClassInstance(); - if (Advisor.class.isInstance(aopObject)) { + if (aopObject instanceof Advisor) { log.debug("adding advisor: " + aopObject.getClass()); Context.removeAdvisor(cls, (Advisor) aopObject); } else { @@ -1210,17 +1200,19 @@ private static boolean isModuleRequiredByAnother(Module dependentModule, String private static ModuleClassLoader removeClassLoader(Module mod) { // create map if it is null - getModuleClassLoaderMap(); - if (!moduleClassLoaders.containsKey(mod)) { + ModuleClassLoader cl = moduleClassLoaders.getIfPresent(mod); + if (cl == null) { log.warn("Module: " + mod.getModuleId() + " does not exist"); } - return moduleClassLoaders.remove(mod); + moduleClassLoaders.invalidate(mod); + + return cl; } /** * Removes module from module repository - * + * * @param mod module to unload */ public static void unloadModule(Module mod) { @@ -1249,7 +1241,7 @@ public static void unloadModule(Module mod) { /** * Return all of the extensions associated with the given pointId Returns empty * extension list if no modules extend this pointId - * + * * @param pointId * @return List of extensions */ @@ -1289,7 +1281,7 @@ public static List getExtensions(String pointId) { /** * Return all of the extensions associated with the given pointId Returns * getExtension(pointId) if no modules extend this pointId for given media type - * + * * @param pointId * @param type Extension.MEDIA_TYPE * @return List of extensions @@ -1307,7 +1299,7 @@ public static List getExtensions(String pointId, Extension.MEDIA_TYPE /** * Get a list of required Privileges defined by the modules - * + * * @return List<Privilege> of the required privileges */ public static List getPrivileges() { @@ -1325,7 +1317,7 @@ public static List getPrivileges() { /** * Get a list of required GlobalProperties defined by the modules - * + * * @return List<GlobalProperty> object of the module's global properties */ public static List getGlobalProperties() { @@ -1343,7 +1335,7 @@ public static List getGlobalProperties() { /** * Checks whether the given module is activated - * + * * @param mod Module to check * @return true if the module is started, false otherwise */ @@ -1353,7 +1345,7 @@ public static boolean isModuleStarted(Module mod) { /** * Checks whether the given module, identified by its id, is started. - * + * * @param moduleId module id. e.g formentry, logic * @since 1.9 * @return true if the module is started, false otherwise @@ -1364,7 +1356,7 @@ public static boolean isModuleStarted(String moduleId) { /** * Get a module's classloader - * + * * @param mod Module to fetch the class loader for * @return ModuleClassLoader pertaining to this module. Returns null if the module is not started * @throws ModuleException if the module does not have a registered classloader @@ -1381,7 +1373,7 @@ public static ModuleClassLoader getModuleClassLoader(Module mod) throws ModuleEx /** * Get a module's classloader via the module id - * + * * @param moduleId String id of the module * @return ModuleClassLoader pertaining to this module. Returns null if the module is not started * @throws ModuleException if this module isn't started or doesn't have a classloader @@ -1398,7 +1390,7 @@ public static ModuleClassLoader getModuleClassLoader(String moduleId) throws Mod /** * Returns all module classloaders This method will not return null - * + * * @return Collection<ModuleClassLoader> all known module classloaders or empty list. */ public static Collection getModuleClassLoaders() { @@ -1412,34 +1404,32 @@ public static Collection getModuleClassLoaders() { /** * Return all current classloaders keyed on module object - * + * * @return Map<Module, ModuleClassLoader> */ public static Map getModuleClassLoaderMap() { + // because the OpenMRS classloader depends on this static function, it is weirdly possible for this to get called + // as this classfile is loaded, in which case, the static final field can be null. if (moduleClassLoaders == null) { - moduleClassLoaders = new WeakHashMap<>(); + return Collections.emptyMap(); } - return moduleClassLoaders; + return moduleClassLoaders.asMap(); } /** * Return the current extension map keyed on extension point id - * + * * @return Map<String, List<Extension>> */ public static Map> getExtensionMap() { - if (extensionMap == null) { - extensionMap = new WeakHashMap<>(); - } - return extensionMap; } /** * Tests whether all modules mentioned in module.requiredModules are loaded and started already (by * being in the startedModules list) - * + * * @param module * @return true/false boolean whether this module's required modules are all started */ @@ -1468,7 +1458,7 @@ private static boolean requiredModulesStarted(Module module) { /** * Update the module: 1) Download the new module 2) Unload the old module 3) Load/start the new * module - * + * * @param mod */ public static Module updateModule(Module mod) throws ModuleException { @@ -1510,7 +1500,7 @@ public static Module updateModule(Module mod) throws ModuleException { * Validates the given token. *

* It is thread safe. - * + * * @param token * @since 1.9.2 */ @@ -1520,9 +1510,9 @@ public static boolean isTokenValid(DaemonToken token) { } else { //We need to synchronize to guarantee that the last passed token is valid. synchronized (daemonTokens) { - DaemonToken validToken = daemonTokens.get(token.getId()); + DaemonToken validToken = daemonTokens.getIfPresent(token.getId()); //Compare by reference to defend from overridden equals. - return validToken == token; + return validToken != null && validToken == token; } } } @@ -1539,7 +1529,7 @@ public static boolean isTokenValid(DaemonToken token) { * previously passed tokens may be invalidated. *

* It is thread safe. - * + * * @param module * @since 1.9.2 */ @@ -1556,27 +1546,25 @@ static void passDaemonToken(Module module) { * when not needed. *

* It is thread safe. - * + * * @param module * @return the token */ private static DaemonToken getDaemonToken(Module module) { - synchronized (daemonTokens) { - DaemonToken token = daemonTokens.get(module.getModuleId()); - if (token != null) { - return token; - } - - token = new DaemonToken(module.getModuleId()); - daemonTokens.put(module.getModuleId(), token); - - return token; + DaemonToken token; + try { + token = daemonTokens.get(module.getModuleId(), () -> new DaemonToken(module.getModuleId())); + } + catch (ExecutionException e) { + throw new APIException(e); } + + return token; } /** * Returns the description for the [moduleId].started global property - * + * * @param moduleId * @return description to use for the .started property */ @@ -1590,7 +1578,7 @@ private static String getGlobalPropertyStartedDescription(String moduleId) { /** * Returns the description for the [moduleId].mandatory global property - * + * * @param moduleId * @return description to use for .mandatory property */ @@ -1605,7 +1593,7 @@ private static String getGlobalPropertyMandatoryModuleDescription(String moduleI /** * Convenience method to save a global property with the given value. Proxy privileges are added so * that this can occur at startup. - * + * * @param key the property for this global property * @param value the value for this global property * @param desc the description @@ -1631,7 +1619,7 @@ private static void saveGlobalProperty(String key, String value, String desc) { /** * Convenience method used to identify module interdependencies and alert the user before modules * are shut down. - * + * * @param moduleId the moduleId used to identify the module being validated * @return List<dependentModules> the list of moduleId's which depend on the module about to * be shutdown. diff --git a/api/src/main/java/org/openmrs/module/ModuleFileParser.java b/api/src/main/java/org/openmrs/module/ModuleFileParser.java index 8d22c406213e..7c68c2f5879f 100644 --- a/api/src/main/java/org/openmrs/module/ModuleFileParser.java +++ b/api/src/main/java/org/openmrs/module/ModuleFileParser.java @@ -29,6 +29,8 @@ import java.util.Objects; import java.util.Set; import java.util.jar.JarFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import org.apache.commons.lang3.StringUtils; @@ -37,6 +39,7 @@ import org.openmrs.api.context.Context; import org.openmrs.customdatatype.CustomDatatype; import org.openmrs.messagesource.MessageSourceService; +import org.openmrs.util.OpenmrsClassLoader; import org.openmrs.util.OpenmrsUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,6 +67,9 @@ public class ModuleFileParser { private static final String OPENMRS_MODULE_FILE_EXTENSION = ".omod"; + //https://resources.openmrs.org/doctype/config-1.5.dtd + private static final Pattern OPENMRS_DTD_SYSTEM_ID_PATTERN = Pattern.compile("https?://resources.openmrs.org/doctype/(?config-[0-9.]+\\.dtd)"); + /** * List out all of the possible version numbers for config files that openmrs has DTDs for. * These are usually stored at http://resources.openmrs.org/doctype/config-x.x.dt @@ -78,6 +84,7 @@ public class ModuleFileParser { validConfigVersions.add("1.4"); validConfigVersions.add("1.5"); validConfigVersions.add("1.6"); + validConfigVersions.add("1.7"); } // TODO - remove this field once ModuleFileParser(File), ModuleFileParser(InputStream) are removed. @@ -286,7 +293,15 @@ private DocumentBuilder newDocumentBuilder() throws ParserConfigurationException // DTD) we return an InputSource // with no data at the end, causing the parser to ignore // the DTD. - db.setEntityResolver((publicId, systemId) -> new InputSource(new StringReader(""))); + db.setEntityResolver((publicId, systemId) -> { + Matcher dtdMatcher = OPENMRS_DTD_SYSTEM_ID_PATTERN.matcher(systemId); + if (dtdMatcher.matches()) { + String dtdFile = dtdMatcher.group("config"); + return new InputSource(OpenmrsClassLoader.getInstance().getResourceAsStream("org/openmrs/module/dtd/" + dtdFile)); + } + return new InputSource(new StringReader("")); + }); + return db; } @@ -375,7 +390,9 @@ private Map extractAwareOfModules(Element configRoot) { } private Map extractStartBeforeModules(Element configRoot) { - return extractModulesWithVersionAttribute(configRoot, "module", "start_before_modules"); + Map result = extractModulesWithVersionAttribute(configRoot, "module", "start_before_modules"); + result.putAll(extractModulesWithVersionAttribute(configRoot, "start_before_module", "start_before_modules")); + return result; } private Map extractModulesWithVersionAttribute(Element configRoot, String elementName, @@ -517,13 +534,17 @@ private GlobalProperty extractGlobalProperty(Element element) { String description = removeTabsAndTrim(getElementTrimmed(element, "description")); String datatypeClassname = getElementTrimmed(element, "datatypeClassname"); String datatypeConfig = getElementTrimmed(element, "datatypeConfig"); + String viewPrivilege = removeTabsAndTrim(getElementTrimmed(element, "viewPrivilege")); + String editPrivilege = removeTabsAndTrim(getElementTrimmed(element, "editPrivilege")); + String deletePrivilege = removeTabsAndTrim(getElementTrimmed(element, "deletePrivilege")); log.debug("property: {}, defaultValue: {}", property, defaultValue); log.debug("description: {}, datatypeClassname: {}", description, datatypeClassname); log.debug("datatypeConfig: {}", datatypeConfig); + log.debug("viewPrivilege: {}, editPrivilege: {}, deletePrivilege: {}", viewPrivilege, editPrivilege, deletePrivilege); return createGlobalProperty(property, defaultValue, description, datatypeClassname, - datatypeConfig); + datatypeConfig, viewPrivilege, editPrivilege, deletePrivilege); } private String removeTabsAndTrim(String string) { @@ -531,7 +552,7 @@ private String removeTabsAndTrim(String string) { } private GlobalProperty createGlobalProperty(String property, String defaultValue, String description, - String datatypeClassname, String datatypeConfig) { + String datatypeClassname, String datatypeConfig, String viewPrivilege, String editPrivilege, String deletePrivilege) { GlobalProperty globalProperty = null; if (property.isEmpty()) { @@ -545,6 +566,17 @@ private GlobalProperty createGlobalProperty(String property, String defaultValue } else { globalProperty = new GlobalProperty(property, defaultValue, description); } + + if (!viewPrivilege.isEmpty()) { + globalProperty.setViewPrivilege(new Privilege(viewPrivilege)); + } + if (!editPrivilege.isEmpty()) { + globalProperty.setEditPrivilege(new Privilege(editPrivilege)); + } + if (!deletePrivilege.isEmpty()) { + globalProperty.setDeletePrivilege(new Privilege(deletePrivilege)); + } + return globalProperty; } diff --git a/api/src/main/java/org/openmrs/module/ModuleUtil.java b/api/src/main/java/org/openmrs/module/ModuleUtil.java index 08e6c0124352..738ffa115066 100644 --- a/api/src/main/java/org/openmrs/module/ModuleUtil.java +++ b/api/src/main/java/org/openmrs/module/ModuleUtil.java @@ -67,7 +67,7 @@ private ModuleUtil() { * * @param props Properties (OpenMRS runtime properties) */ - public static void startup(Properties props) throws ModuleMustStartException, OpenmrsCoreModuleException { + public static void startup(Properties props) throws ModuleMustStartException { String moduleListString = props.getProperty(ModuleConstants.RUNTIMEPROPERTY_MODULE_LIST_TO_LOAD); @@ -164,10 +164,10 @@ public static void shutdown() { log.debug("done shutting down modules"); // clean up the static variables just in case they weren't done before - ModuleFactory.extensionMap = null; - ModuleFactory.loadedModules = null; - ModuleFactory.moduleClassLoaders = null; - ModuleFactory.startedModules = null; + ModuleFactory.extensionMap.clear(); + ModuleFactory.loadedModules.invalidateAll(); + ModuleFactory.moduleClassLoaders.invalidateAll(); + ModuleFactory.startedModules.invalidateAll(); } /** @@ -186,9 +186,7 @@ public static File insertModuleFile(InputStream inputStream, String filename) { File file = new File(folder.getAbsolutePath(), filename); - FileOutputStream outputStream = null; - try { - outputStream = new FileOutputStream(file); + try (FileOutputStream outputStream = new FileOutputStream(file)) { OpenmrsUtil.copyFile(inputStream, outputStream); } catch (IOException e) { @@ -199,10 +197,6 @@ public static File insertModuleFile(InputStream inputStream, String filename) { inputStream.close(); } catch (Exception e) { /* pass */} - try { - outputStream.close(); - } - catch (Exception e) { /* pass */} } return file; @@ -222,7 +216,6 @@ public static File insertModuleFile(InputStream inputStream, String filename) { * Should return false if current openmrs version does not match any element in versions */ public static boolean isOpenmrsVersionInVersions(String ...versions) { - if (versions == null || versions.length == 0) { return false; } @@ -526,9 +519,6 @@ public static URL file2url(final File file) throws MalformedURLException { try { return file.getCanonicalFile().toURI().toURL(); } - catch (MalformedURLException mue) { - throw mue; - } catch (IOException | NoSuchMethodError ioe) { throw new MalformedURLException("Cannot convert: " + file.getName() + " to url"); } @@ -553,11 +543,8 @@ public static URL file2url(final File file) throws MalformedURLException { * Should expand file with parent tree if name is file and keepFullPath is true */ public static void expandJar(File fileToExpand, File tmpModuleDir, String name, boolean keepFullPath) throws IOException { - JarFile jarFile = null; - InputStream input = null; String docBase = tmpModuleDir.getAbsolutePath(); - try { - jarFile = new JarFile(fileToExpand); + try (JarFile jarFile = new JarFile(fileToExpand)) { Enumeration jarEntries = jarFile.entries(); boolean foundName = (name == null); @@ -582,10 +569,9 @@ public static void expandJar(File fileToExpand, File tmpModuleDir, String name, if (entryName.endsWith("/") || "".equals(entryName)) { continue; } - input = jarFile.getInputStream(jarEntry); - expand(input, docBase, entryName); - input.close(); - input = null; + try(InputStream input = jarFile.getInputStream(jarEntry)) { + expand(input, docBase, entryName); + } foundName = true; } } @@ -598,16 +584,6 @@ public static void expandJar(File fileToExpand, File tmpModuleDir, String name, log.warn("Unable to delete tmpModuleFile on error", e); throw e; } - finally { - try { - input.close(); - } - catch (Exception e) { /* pass */} - try { - jarFile.close(); - } - catch (Exception e) { /* pass */} - } } /** @@ -625,17 +601,9 @@ private static File expand(InputStream input, String fileDir, String name) throw log.debug("expanding: {}", name); File file = new File(fileDir, name); - FileOutputStream outStream = null; - try { - outStream = new FileOutputStream(file); + try (FileOutputStream outStream = new FileOutputStream(file)) { OpenmrsUtil.copyFile(input, outStream); } - finally { - try { - outStream.close(); - } - catch (Exception e) { /* pass */} - } return file; } diff --git a/api/src/main/java/org/openmrs/notification/db/hibernate/HibernateAlertDAO.java b/api/src/main/java/org/openmrs/notification/db/hibernate/HibernateAlertDAO.java index 4853d69d42d6..7a4cb4edc426 100644 --- a/api/src/main/java/org/openmrs/notification/db/hibernate/HibernateAlertDAO.java +++ b/api/src/main/java/org/openmrs/notification/db/hibernate/HibernateAlertDAO.java @@ -9,14 +9,17 @@ */ package org.openmrs.notification.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; -import org.hibernate.Criteria; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Restrictions; import org.openmrs.User; import org.openmrs.api.db.DAOException; import org.openmrs.notification.Alert; @@ -62,7 +65,7 @@ public Alert saveAlert(Alert alert) throws DAOException { */ @Override public Alert getAlert(Integer alertId) throws DAOException { - return (Alert) sessionFactory.getCurrentSession().get(Alert.class, alertId); + return sessionFactory.getCurrentSession().get(Alert.class, alertId); } /** @@ -77,54 +80,65 @@ public void deleteAlert(Alert alert) throws DAOException { * @see org.openmrs.notification.AlertService#getAllAlerts(boolean) */ @Override - @SuppressWarnings("unchecked") public List getAllAlerts(boolean includeExpired) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Alert.class); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Alert.class); + Root root = cq.from(Alert.class); + // exclude the expired alerts unless requested if (!includeExpired) { - crit.add(Restrictions.or(Restrictions.isNull("dateToExpire"), Restrictions.gt("dateToExpire", new Date()))); + cq.where(cb.or( + cb.isNull(root.get("dateToExpire")), + cb.greaterThan(root.get("dateToExpire"), new Date())) + ); } - - return crit.list(); + + return session.createQuery(cq).getResultList(); } - + /** * @see org.openmrs.notification.db.AlertDAO#getAlerts(org.openmrs.User, boolean, boolean) */ @Override - @SuppressWarnings("unchecked") public List getAlerts(User user, boolean includeRead, boolean includeExpired) throws DAOException { log.debug("Getting alerts for user " + user + " read? " + includeRead + " expired? " + includeExpired); - - Criteria crit = sessionFactory.getCurrentSession().createCriteria(Alert.class, "alert"); - + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Alert.class); + Root root = cq.from(Alert.class); + + List predicates = new ArrayList<>(); + if (user != null && user.getUserId() != null) { - crit.createCriteria("recipients", "recipient"); - crit.add(Restrictions.eq("recipient.recipient", user)); + predicates.add(cb.equal(root.join("recipients").get("recipientId"), user.getUserId())); } else { // getting here means we passed in no user or a blank user. // a null recipient column means get stuff for the anonymous user - + // returning an empty list for now because the above throws an error. // we may need to remodel how recipients are handled to get anonymous users alerts return Collections.emptyList(); } - + // exclude the expired alerts unless requested if (!includeExpired) { - crit.add(Restrictions.or(Restrictions.isNull("dateToExpire"), Restrictions.gt("dateToExpire", new Date()))); + Predicate dateToExpireIsNull = cb.isNull(root.get("dateToExpire")); + Predicate dateToExpireIsGreater = cb.greaterThan(root.get("dateToExpire"), new Date()); + predicates.add(cb.or(dateToExpireIsNull, dateToExpireIsGreater)); } - + // exclude the read alerts unless requested if (!includeRead && user.getUserId() != null) { - crit.add(Restrictions.eq("alertRead", false)); - crit.add(Restrictions.eq("recipient.alertRead", false)); + predicates.add(cb.isFalse(root.get("alertRead"))); + predicates.add(cb.isFalse(root.join("recipients").get("alertRead"))); } - - crit.addOrder(Order.desc("dateChanged")); - - return crit.list(); + + cq.where(predicates.toArray(new Predicate[]{})) + .orderBy(cb.desc(root.get("dateChanged"))); + + return session.createQuery(cq).getResultList(); } - + } diff --git a/api/src/main/java/org/openmrs/notification/mail/velocity/VelocityMessagePreparator.java b/api/src/main/java/org/openmrs/notification/mail/velocity/VelocityMessagePreparator.java index 1517b5205fa4..588341b742c9 100644 --- a/api/src/main/java/org/openmrs/notification/mail/velocity/VelocityMessagePreparator.java +++ b/api/src/main/java/org/openmrs/notification/mail/velocity/VelocityMessagePreparator.java @@ -10,9 +10,11 @@ package org.openmrs.notification.mail.velocity; import java.io.StringWriter; +import java.util.Properties; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.log.Log4JLogChute; import org.openmrs.notification.Message; import org.openmrs.notification.MessageException; import org.openmrs.notification.MessagePreparator; @@ -40,7 +42,11 @@ public class VelocityMessagePreparator implements MessagePreparator { public VelocityMessagePreparator() throws MessageException { try { engine = new VelocityEngine(); - engine.init(); + Properties props = new Properties(); + props.put("runtime.log.logsystem.class", Log4JLogChute.class.getName()); + props.put("runtime.log.logsystem.log4j.category", "velocity"); + props.put("runtime.log.logsystem.log4j.logger", "velocity"); + engine.init(props); } catch (Exception e) { log.error("Failed to create velocity engine " + e.getMessage(), e); diff --git a/api/src/main/java/org/openmrs/scheduler/db/hibernate/HibernateSchedulerDAO.java b/api/src/main/java/org/openmrs/scheduler/db/hibernate/HibernateSchedulerDAO.java index 4bb59b427add..e1ebe191bc8f 100644 --- a/api/src/main/java/org/openmrs/scheduler/db/hibernate/HibernateSchedulerDAO.java +++ b/api/src/main/java/org/openmrs/scheduler/db/hibernate/HibernateSchedulerDAO.java @@ -9,12 +9,15 @@ */ package org.openmrs.scheduler.db.hibernate; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; import java.util.List; -import org.hibernate.Criteria; +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Restrictions; import org.openmrs.api.db.DAOException; +import org.openmrs.api.db.hibernate.HibernateUtil; import org.openmrs.scheduler.Schedule; import org.openmrs.scheduler.TaskDefinition; import org.openmrs.scheduler.db.SchedulerDAO; @@ -72,7 +75,7 @@ public void createTask(TaskDefinition task) throws DAOException { */ @Override public TaskDefinition getTask(Integer taskId) throws DAOException { - TaskDefinition task = (TaskDefinition) sessionFactory.getCurrentSession().get(TaskDefinition.class, taskId); + TaskDefinition task = sessionFactory.getCurrentSession().get(TaskDefinition.class, taskId); if (task == null) { log.warn("Task '" + taskId + "' not found"); @@ -90,18 +93,22 @@ public TaskDefinition getTask(Integer taskId) throws DAOException { */ @Override public TaskDefinition getTaskByName(String name) throws DAOException { - Criteria crit = sessionFactory.getCurrentSession().createCriteria(TaskDefinition.class).add( - Restrictions.eq("name", name)); - - TaskDefinition task = (TaskDefinition) crit.uniqueResult(); - + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(TaskDefinition.class); + Root root = cq.from(TaskDefinition.class); + + cq.where(cb.equal(root.get("name"), name)); + + TaskDefinition task = session.createQuery(cq).uniqueResult(); + if (task == null) { log.warn("Task '" + name + "' not found"); throw new ObjectRetrievalFailureException(TaskDefinition.class, name); } return task; } - + /** * Update task * @@ -120,11 +127,15 @@ public void updateTask(TaskDefinition task) throws DAOException { * @throws DAOException */ @Override - @SuppressWarnings("unchecked") public List getTasks() throws DAOException { - return sessionFactory.getCurrentSession().createCriteria(TaskDefinition.class).list(); + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(TaskDefinition.class); + cq.from(TaskDefinition.class); + + return session.createQuery(cq).getResultList(); } - + /** * Delete task from database. * @@ -157,7 +168,7 @@ public void deleteTask(TaskDefinition taskConfig) throws DAOException { */ @Override public Schedule getSchedule(Integer scheduleId) throws DAOException { - Schedule schedule = (Schedule) sessionFactory.getCurrentSession().get(Schedule.class, scheduleId); + Schedule schedule = sessionFactory.getCurrentSession().get(Schedule.class, scheduleId); if (schedule == null) { log.error("Schedule '" + scheduleId + "' not found"); @@ -172,7 +183,6 @@ public Schedule getSchedule(Integer scheduleId) throws DAOException { */ @Override public TaskDefinition getTaskByUuid(String uuid) throws DAOException { - return (TaskDefinition) sessionFactory.getCurrentSession() - .createQuery("from TaskDefinition o where o.uuid = :uuid").setString("uuid", uuid).uniqueResult(); + return HibernateUtil.getUniqueEntityByUUID(sessionFactory, TaskDefinition.class, uuid); } } diff --git a/api/src/main/java/org/openmrs/util/ClassLoaderFileOpener.java b/api/src/main/java/org/openmrs/util/ClassLoaderFileOpener.java deleted file mode 100644 index 60c040ead9f3..000000000000 --- a/api/src/main/java/org/openmrs/util/ClassLoaderFileOpener.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public License, - * v. 2.0. If a copy of the MPL was not distributed with this file, You can - * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under - * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. - * - * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS - * graphic logo is a trademark of OpenMRS Inc. - */ -package org.openmrs.util; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.SortedSet; - -import liquibase.resource.AbstractResourceAccessor; -import liquibase.resource.InputStreamList; - -/** - * Implementation of liquibase FileOpener interface so that the {@link OpenmrsClassLoader} will be - * used to find files (or any other classloader that is passed into the constructor). This allows - * liquibase xml files in modules to be found. - */ -public class ClassLoaderFileOpener extends AbstractResourceAccessor { - - /** - * The classloader to read from - */ - private final ClassLoader cl; - - /** - * @param cl the {@link ClassLoader} to use for finding files. - */ - public ClassLoaderFileOpener(ClassLoader cl) { - this.cl = cl; - } - - @Override - public InputStreamList openStreams(String context, String path) throws IOException { - InputStreamList result = new InputStreamList(); - - if (path.isEmpty()) { - return result; - } - - URL url = cl.getResource(path); - if (url != null) { - try { - result.add(url.toURI(), url.openStream()); - } - catch (URISyntaxException e) { - throw new IOException(e); - } - } - - return result; - } - - @Override - public SortedSet list(String s, String s1, boolean b, boolean b1, boolean b2) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public SortedSet describeLocations() { - return null; - } -} diff --git a/api/src/main/java/org/openmrs/util/DatabaseUpdater.java b/api/src/main/java/org/openmrs/util/DatabaseUpdater.java index 790405427bae..b7a6dcb7009e 100644 --- a/api/src/main/java/org/openmrs/util/DatabaseUpdater.java +++ b/api/src/main/java/org/openmrs/util/DatabaseUpdater.java @@ -40,6 +40,7 @@ import org.openmrs.liquibase.ChangeLogVersionFinder; import org.openmrs.liquibase.ChangeSetExecutorCallback; import org.openmrs.liquibase.LiquibaseProvider; +import org.openmrs.liquibase.OpenmrsClassLoaderResourceAccessor; import org.openmrs.module.ModuleClassLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -879,7 +880,7 @@ private static CompositeResourceAccessor getCompositeResourceAccessor(ClassLoade } } - ResourceAccessor openmrsFO = new ClassLoaderFileOpener(classLoader); + ResourceAccessor openmrsFO = new OpenmrsClassLoaderResourceAccessor(classLoader); ResourceAccessor fsFO = new FileSystemResourceAccessor(OpenmrsUtil.getApplicationDataDirectoryAsFile()); return new CompositeResourceAccessor(openmrsFO, fsFO); } diff --git a/api/src/main/java/org/openmrs/util/LocaleUtility.java b/api/src/main/java/org/openmrs/util/LocaleUtility.java index a39a04f03c43..547c1e0384d4 100644 --- a/api/src/main/java/org/openmrs/util/LocaleUtility.java +++ b/api/src/main/java/org/openmrs/util/LocaleUtility.java @@ -15,6 +15,7 @@ import java.util.MissingResourceException; import java.util.Set; +import org.apache.commons.lang3.LocaleUtils; import org.openmrs.GlobalProperty; import org.openmrs.api.GlobalPropertyListener; import org.openmrs.api.context.Context; @@ -127,18 +128,33 @@ public static boolean areCompatible(Locale lhs, Locale rhs) { * Should get locale from language code country code and variant */ public static Locale fromSpecification(String localeSpecification) { - Locale createdLocale = null; + Locale createdLocale; localeSpecification = localeSpecification.trim(); + try { + createdLocale = LocaleUtils.toLocale(localeSpecification); + } catch (IllegalArgumentException e) { + if (localeSpecification.matches("[a-zA-Z]{2}[-_][a-zA-Z]{2,}")) { + return null; + } else { + createdLocale = generateLocaleFromLegacyFormat(localeSpecification); + } + } + + return createdLocale; + } + + private static Locale generateLocaleFromLegacyFormat(String localeSpecification) { + Locale createdLocale = null; + String[] localeComponents = localeSpecification.split("_"); if (localeComponents.length == 1) { createdLocale = new Locale(localeComponents[0]); } else if (localeComponents.length == 2) { createdLocale = new Locale(localeComponents[0], localeComponents[1]); } else if (localeComponents.length > 2) { - String variant = localeSpecification.substring(localeSpecification.indexOf(localeComponents[2])); // gets everything after the - // second underscore + String variant = localeSpecification.substring(localeSpecification.indexOf(localeComponents[2])); createdLocale = new Locale(localeComponents[0], localeComponents[1], variant); } diff --git a/api/src/main/java/org/openmrs/util/OpenmrsConstants.java b/api/src/main/java/org/openmrs/util/OpenmrsConstants.java index c2d0d3024dba..ff8cc1389079 100644 --- a/api/src/main/java/org/openmrs/util/OpenmrsConstants.java +++ b/api/src/main/java/org/openmrs/util/OpenmrsConstants.java @@ -9,20 +9,21 @@ */ package org.openmrs.util; +import static java.util.Arrays.asList; + import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; -import java.util.ArrayList; import java.util.Locale; import java.util.Map; import java.util.Properties; import org.apache.commons.io.IOUtils; import org.openmrs.GlobalProperty; -import org.openmrs.api.context.Context; import org.openmrs.api.handler.ExistingVisitAssignmentHandler; import org.openmrs.customdatatype.datatype.BooleanDatatype; import org.openmrs.customdatatype.datatype.FreeTextDatatype; @@ -34,8 +35,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static java.util.Arrays.asList; - /** * Constants used in OpenMRS. Contents built from build properties (version, version_short, and * expected_database). Some are set at runtime (database, database version). This file should @@ -128,7 +127,7 @@ public static String getOpenmrsProperty(String property) { return null; } - + public static String DATABASE_NAME = "openmrs"; public static String DATABASE_BUSINESS_NAME = "openmrs"; @@ -179,7 +178,7 @@ public static String getOpenmrsProperty(String property) { /** * These words are ignored in concept and patient searches - * + * * @return Collection<String> of words that are ignored */ public static final Collection STOP_WORDS() { @@ -202,7 +201,7 @@ public static final Collection STOP_WORDS() { * A gender character to gender name map
* TODO issues with localization. How should this be handled? * @deprecated As of 2.2, replaced by {@link #GENDERS} - * + * * @return Map<String, String> of gender character to gender name */ @Deprecated @@ -218,10 +217,10 @@ public static final Map GENDER() { * A list of 1-letter strings representing genders */ public static final List GENDERS = Collections.unmodifiableList(asList("M", "F")); - + /** * These roles are given to a user automatically and cannot be assigned - * + * * @return Collection<String> of the auto-assigned roles */ public static final Collection AUTO_ROLES() { @@ -363,6 +362,8 @@ public static final Collection AUTO_ROLES() { public static final String GLOBAL_PROPERTY_LAYOUT_NAME_FORMAT = "layout.name.format"; + public static final String GLOBAL_PROPERTY_LAYOUT_NAME_TEMPLATE = "layout.name.template"; + public static final String GLOBAL_PROPERTY_ENCOUNTER_TYPES_LOCKED = "EncounterType.encounterTypes.locked"; public static final String GLOBAL_PROPERTY_FORMS_LOCKED = "forms.locked"; @@ -568,6 +569,14 @@ public static final Collection AUTO_ROLES() { public static final String AUTO_CLOSE_VISITS_TASK_NAME = "Auto Close Visits Task"; public static final String GP_ALLOWED_FAILED_LOGINS_BEFORE_LOCKOUT = "security.allowedFailedLoginsBeforeLockout"; + + public static final String GP_UNLOCK_ACCOUNT_WAITING_TIME = "security.unlockAccountWaitingTime"; + + /** + * Time in minutes needed for re-activation of a user account locked due to successive entry of + * incorrect login credentials + */ + public static final String GP_USER_UNLOCK_WAIT_TIME = "security.userUnlockWaitTime"; /** * @since 1.9.9, 1.10.2, 1.11 @@ -604,7 +613,7 @@ public static final Collection AUTO_ROLES() { /** * Indicates the version of the search index. The index will be rebuilt, if the version changes. - * + * * @since 1.11 */ public static final Integer SEARCH_INDEX_VERSION = 7; @@ -635,7 +644,7 @@ public static final Collection AUTO_ROLES() { /** * At OpenMRS startup these global properties/default values/descriptions are inserted into the * database if they do not exist yet. - * + * * @return List<GlobalProperty> of the core global properties */ public static final List CORE_GLOBAL_PROPERTIES() { @@ -984,6 +993,9 @@ public static final List CORE_GLOBAL_PROPERTIES() { props.add(new GlobalProperty(GP_ALLOWED_FAILED_LOGINS_BEFORE_LOCKOUT, "7", "Maximum number of failed logins allowed after which username is locked out")); + props.add(new GlobalProperty(GP_USER_UNLOCK_WAIT_TIME, "5", + "Time in minutes needed for re-activation of a user account locked due to successive entry of incorrect login credentials")); + props.add(new GlobalProperty(GP_DEFAULT_CONCEPT_MAP_TYPE, "NARROWER-THAN", "Default concept map type which is used when no other is set")); @@ -1224,14 +1236,14 @@ public static final Collection CONCEPT_PROPOSAL_STATES() { /** * It points to a directory where 'openmrs.log' is stored. - * + * * @since 1.9.2 */ public static final String GP_LOG_LOCATION = "log.location"; /** * It specifies a log layout pattern used by the OpenMRS file appender. - * + * * @since 1.9.2 */ public static final String GP_LOG_LAYOUT = "log.layout"; @@ -1266,13 +1278,13 @@ public static final Collection CONCEPT_PROPOSAL_STATES() { /** * Default url responsible for authentication if a user is not logged in. - * + * * @see #GP_LOGIN_URL */ public static final String LOGIN_URL = "login.htm"; /** - * Global property name that defines the default url + * Global property name that defines the default url * responsible for authentication if user is not logged in. * * @see #LOGIN_URL @@ -1282,7 +1294,7 @@ public static final Collection CONCEPT_PROPOSAL_STATES() { /** * These enumerations should be used in ObsService and PersonService getters to help determine * which type of object to restrict on - * + * * @see org.openmrs.api.ObsService * @see org.openmrs.api.PersonService */ diff --git a/api/src/main/java/org/openmrs/util/OpenmrsJacksonLocaleModule.java b/api/src/main/java/org/openmrs/util/OpenmrsJacksonLocaleModule.java new file mode 100644 index 000000000000..2ce9a5a0942f --- /dev/null +++ b/api/src/main/java/org/openmrs/util/OpenmrsJacksonLocaleModule.java @@ -0,0 +1,83 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.util; + +import java.io.IOException; +import java.util.Locale; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.Serializers; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +/** + * This is a Jackson-Databind module that simply changes how we serialize locales by pre-adopting the Jackson 3.0 convention + * of using toLanguageTag() instead of toString(). When Jackson 3.0 is available, we should be able to drop this class. + *

+ * This module is available to be used by any use-case that creates an ObjectMapper. However, it is only registered by default + * for the Spring MappingJackson2HttpMessageConverter class. + */ +public class OpenmrsJacksonLocaleModule extends Module { + + private static final String MODULE_NAME = "openmrs-locale"; + + private static final Version VERSION = new Version(1, 0, 0, null, "org.openmrs.web", "openmrs-locale"); + + @Override + public String getModuleName() { + return MODULE_NAME; + } + + @Override + public Version version() { + return VERSION; + } + + @Override + public void setupModule(SetupContext setupContext) { + setupContext.addSerializers(new Serializers.Base() { + + @Override + @SuppressWarnings("unchecked") + public JsonSerializer findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) { + + final Class raw = type.getRawClass(); + if (Locale.class.isAssignableFrom(raw)) { + return new OpenmrsLocaleSerializer((Class) raw); + } + + return super.findSerializer(config, type, beanDesc); + } + }); + } + + private static class OpenmrsLocaleSerializer extends StdSerializer { + + protected OpenmrsLocaleSerializer(Class t) { + super(t, false); + } + + @Override + public void serialize(Locale locale, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException { + if (locale == Locale.ROOT) { + jsonGenerator.writeString(""); + } else { + jsonGenerator.writeString(locale.toLanguageTag()); + } + } + } +} diff --git a/api/src/main/java/org/openmrs/util/PrivilegeConstants.java b/api/src/main/java/org/openmrs/util/PrivilegeConstants.java index 0a4d4b25a7ba..9312f4d41c3a 100644 --- a/api/src/main/java/org/openmrs/util/PrivilegeConstants.java +++ b/api/src/main/java/org/openmrs/util/PrivilegeConstants.java @@ -109,6 +109,9 @@ private PrivilegeConstants() { @AddOnStartup(description = "Able to get person attribute types") public static final String GET_PERSON_ATTRIBUTE_TYPES = "Get Person Attribute Types"; + @AddOnStartup(description = "Able to get provider attribute types") + public static final String GET_PROVIDER_ATTRIBUTE_TYPES = "Get Provider Attribute Types"; + @AddOnStartup(description = "Able to get person objects") public static final String GET_PERSONS = "Get People"; diff --git a/api/src/main/java/org/openmrs/util/Reflect.java b/api/src/main/java/org/openmrs/util/Reflect.java index e0495ba791d9..e389d2d35226 100644 --- a/api/src/main/java/org/openmrs/util/Reflect.java +++ b/api/src/main/java/org/openmrs/util/Reflect.java @@ -9,6 +9,7 @@ */ package org.openmrs.util; + import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; @@ -16,12 +17,8 @@ import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.List; -import org.azeckoski.reflectutils.ClassDataCacher; -import org.azeckoski.reflectutils.ClassFields; -import org.azeckoski.reflectutils.exceptions.FieldnameNotFoundException; /** * This class has convenience methods to find the fields on a class and superclass as well as @@ -29,6 +26,7 @@ */ public class Reflect { + private Class parametrizedClass; /** @@ -72,8 +70,16 @@ public static boolean isCollection(Object object) { * Should return all fields include private and super classes */ public static List getAllFields(Class fieldClass) { - List fields = ClassDataCacher.getInstance().getClassData(fieldClass).getFields(); - return new ArrayList<>(fields); + List fields = new ArrayList<>(); + while (fieldClass != null) { + Field[] declaredFields = fieldClass.getDeclaredFields(); + for (Field field : declaredFields) { + field.setAccessible(true); + fields.add(field); + } + fieldClass = fieldClass.getSuperclass(); + } + return fields; } /** @@ -85,11 +91,11 @@ public static List getAllFields(Class fieldClass) { * @return true if the given annotation is present */ public static boolean isAnnotationPresent(Class fieldClass, String fieldName, Class annotation) { - ClassFields classFields = ClassDataCacher.getInstance().getClassFields(fieldClass); try { - return classFields.getFieldAnnotation(annotation, fieldName) != null; - } catch (FieldnameNotFoundException e) { - return false; + Field field = fieldClass.getDeclaredField(fieldName); + return field.isAnnotationPresent(annotation); + } catch (NoSuchFieldException e) { + return false; } } diff --git a/api/src/main/java/org/openmrs/util/databasechange/SourceMySqldiffFile.java b/api/src/main/java/org/openmrs/util/databasechange/SourceMySqldiffFile.java index 84d68067db53..5b086f0aa3a0 100644 --- a/api/src/main/java/org/openmrs/util/databasechange/SourceMySqldiffFile.java +++ b/api/src/main/java/org/openmrs/util/databasechange/SourceMySqldiffFile.java @@ -19,7 +19,7 @@ import liquibase.resource.InputStreamList; import liquibase.resource.ResourceAccessor; import org.openmrs.api.context.Context; -import org.openmrs.util.ClassLoaderFileOpener; +import org.openmrs.liquibase.OpenmrsClassLoaderResourceAccessor; import org.openmrs.util.OpenmrsClassLoader; import org.openmrs.util.OpenmrsConstants; import org.openmrs.util.OpenmrsUtil; @@ -30,7 +30,6 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; @@ -40,7 +39,6 @@ import java.util.Collections; import java.util.List; import java.util.Properties; -import java.util.Set; /** * Executes (aka "source"s) the given file on the current database.
@@ -94,7 +92,7 @@ public void execute(Database database) throws CustomChangeException { try { tmpOutputFile = File.createTempFile(sqlFile, "tmp"); - fileOpener = new ClassLoaderFileOpener(OpenmrsClassLoader.getInstance()); + fileOpener = new OpenmrsClassLoaderResourceAccessor(OpenmrsClassLoader.getInstance()); try (InputStreamList sqlFileInputStream = fileOpener.openStreams(null, sqlFile); OutputStream outputStream = new FileOutputStream(tmpOutputFile)) { if (sqlFileInputStream != null && !sqlFileInputStream.isEmpty()) { diff --git a/api/src/main/java/org/openmrs/validator/ObsValidator.java b/api/src/main/java/org/openmrs/validator/ObsValidator.java index aeb6558129d1..fb3998c8568e 100644 --- a/api/src/main/java/org/openmrs/validator/ObsValidator.java +++ b/api/src/main/java/org/openmrs/validator/ObsValidator.java @@ -101,8 +101,9 @@ private void validateHelper(Obs obs, Errors errors, List ancestors, boolean errors.rejectValue("obsDatetime", "error.null"); } + boolean isObsGroup = obs.hasGroupMembers(true); // if this is an obs group (i.e., parent) make sure that it has no values (other than valueGroupId) set - if (obs.hasGroupMembers()) { + if (isObsGroup) { if (obs.getValueCoded() != null) { errors.rejectValue("valueCoded", "error.not.null"); } @@ -150,7 +151,7 @@ else if (obs.getValueBoolean() == null && obs.getValueCoded() == null && obs.get errors.rejectValue("concept", "error.null"); } // if there is a concept, and this isn't a group, perform validation tests specific to the concept datatype - else if (!obs.hasGroupMembers()) { + else if (!isObsGroup) { ConceptDatatype dt = c.getDatatype(); if (dt != null) { if (dt.isBoolean() && obs.getValueBoolean() == null) { diff --git a/api/src/main/java/org/openmrs/validator/ProviderAttributeTypeValidator.java b/api/src/main/java/org/openmrs/validator/ProviderAttributeTypeValidator.java index 67b653b35097..7334c9fb33dd 100644 --- a/api/src/main/java/org/openmrs/validator/ProviderAttributeTypeValidator.java +++ b/api/src/main/java/org/openmrs/validator/ProviderAttributeTypeValidator.java @@ -11,7 +11,10 @@ import org.openmrs.ProviderAttributeType; import org.openmrs.annotation.Handler; +import org.openmrs.api.ProviderService; +import org.openmrs.api.context.Context; import org.springframework.validation.Errors; +import org.springframework.validation.ValidationUtils; /** * Validates attributes on the {@link ProviderAttributeType} object. @@ -33,12 +36,36 @@ public boolean supports(Class c) { return ProviderAttributeType.class.isAssignableFrom(c); } + /** + * @see org.springframework.validation.Validator#validate(java.lang.Object, + * org.springframework.validation.Errors) + * Should fail validation if name is null + * Should fail validation if datatypeClassname is empty + * Should fail validation if name already in use + * Should pass validation if description is null or empty or whitespace + * Should pass validation if all fields are correct + * Should pass validation if field lengths are correct + * + * NOTE: the current behaviour of the name is that;- when you create an attribute with a name "test", you cannot + * create another one with the same name not until you retire the first one. When you retire "test", you + * create a new one with the name "test" since the existing one has been retired. + */ @Override public void validate(Object obj, Errors errors) { if (obj != null) { super.validate(obj, errors); ValidateUtil.validateFieldLengths(errors, obj.getClass(), "name", "description", "datatypeClassname", - "preferredHandlerClassname", "retireReason"); + "preferredHandlerClassname", "retireReason"); + ProviderAttributeType type = (ProviderAttributeType) obj; + ValidationUtils.rejectIfEmpty(errors, "name", "ProviderAttributeType.error.nameEmpty"); + ValidationUtils.rejectIfEmpty(errors, "datatypeClassname", "ProviderAttributeType.error.datatypeEmpty"); + ProviderService service = Context.getProviderService(); + ProviderAttributeType attributeType = service.getProviderAttributeTypeByName(type.getName()); + if (attributeType != null) { + if (!attributeType.getUuid().equals(type.getUuid()) && !attributeType.getRetired()) { + errors.rejectValue("name", "ProviderAttributeType.error.nameAlreadyInUse"); + } + } } } } diff --git a/api/src/main/resources/hibernate.cfg.xml b/api/src/main/resources/hibernate.cfg.xml index ab2c31144581..c40eced51978 100644 --- a/api/src/main/resources/hibernate.cfg.xml +++ b/api/src/main/resources/hibernate.cfg.xml @@ -55,11 +55,8 @@ - - - @@ -68,7 +65,6 @@ - @@ -82,9 +78,7 @@ - - @@ -93,10 +87,8 @@ - - diff --git a/api/src/main/resources/hibernate.default.properties b/api/src/main/resources/hibernate.default.properties index 4f26e7e03f91..253719630e9c 100644 --- a/api/src/main/resources/hibernate.default.properties +++ b/api/src/main/resources/hibernate.default.properties @@ -28,6 +28,8 @@ hibernate.cache.use_structured_entries=false hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory hibernate.cache.use_second_level_cache=true +hibernate.cache.use_query_cache=true + hibernate.search.default.directory_provider=filesystem hibernate.search.default.indexBase=%APPLICATION_DATA_DIRECTORY%/lucene/indexes hibernate.search.default.locking_strategy=single diff --git a/api/src/main/resources/liquibase-core-data.xml b/api/src/main/resources/liquibase-core-data.xml index ea03b705a096..d57811e67617 100644 --- a/api/src/main/resources/liquibase-core-data.xml +++ b/api/src/main/resources/liquibase-core-data.xml @@ -23,6 +23,6 @@ http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> - + diff --git a/api/src/main/resources/liquibase-schema-only.xml b/api/src/main/resources/liquibase-schema-only.xml index cafa8ed30e66..86c3735f8ed6 100644 --- a/api/src/main/resources/liquibase-schema-only.xml +++ b/api/src/main/resources/liquibase-schema-only.xml @@ -23,6 +23,6 @@ http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> - + diff --git a/api/src/main/resources/liquibase-update-to-latest-from-1.9.x.xml b/api/src/main/resources/liquibase-update-to-latest-from-1.9.x.xml index da600633cc2d..63351df6b1a9 100644 --- a/api/src/main/resources/liquibase-update-to-latest-from-1.9.x.xml +++ b/api/src/main/resources/liquibase-update-to-latest-from-1.9.x.xml @@ -30,5 +30,6 @@ + diff --git a/api/src/main/resources/liquibase-update-to-latest.xml b/api/src/main/resources/liquibase-update-to-latest.xml index 6cd7c56fb6c6..d9f7dd0f83b6 100644 --- a/api/src/main/resources/liquibase-update-to-latest.xml +++ b/api/src/main/resources/liquibase-update-to-latest.xml @@ -23,6 +23,6 @@ http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> - + diff --git a/api/src/main/resources/messages.properties b/api/src/main/resources/messages.properties index 7724f58902e7..7d5561d5118a 100644 --- a/api/src/main/resources/messages.properties +++ b/api/src/main/resources/messages.properties @@ -291,6 +291,7 @@ searchWidget.noResultsFoundFor=No results found for: {0}, here are partia admin.title.short=Admin admin.title=Administration +admin.password.locked=Admin password is locked and can only be changed via runtime property. auth.logged.out=You are now logged out auth.session.expired=Your session has expired. @@ -1247,6 +1248,9 @@ GlobalProperty.property.deleted=Property "{0}" deleted GlobalProperty.property.notDeleted=Could not delete property "{0}" GlobalProperty.add=Add Property GlobalProperty.error.name.required=Name required for new global property +GlobalProperty.error.privilege.required.edit=Privilege: {0}, required to edit globalProperty: {1} +GlobalProperty.error.privilege.required.purge=Privilege: {0}, required to purge globalProperty: {1} +GlobalProperty.error.privilege.required.view=Privilege: {0}, required to view globalProperty: {1} GlobalProperty.saved=Global properties saved GlobalProperty.not.saved=Global properties not saved GlobalProperty.toDelete=Tagged for Deletion! diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/DrugReferenceMap.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/DrugReferenceMap.hbm.xml index e36818041321..5248f9dde87b 100644 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/DrugReferenceMap.hbm.xml +++ b/api/src/main/resources/org/openmrs/api/db/hibernate/DrugReferenceMap.hbm.xml @@ -17,7 +17,7 @@ - + drug_reference_map_drug_reference_map_id_seq @@ -27,7 +27,7 @@ - + diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/GlobalProperty.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/GlobalProperty.hbm.xml index bc603f5146ae..f254bdd91541 100644 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/GlobalProperty.hbm.xml +++ b/api/src/main/resources/org/openmrs/api/db/hibernate/GlobalProperty.hbm.xml @@ -36,6 +36,12 @@ + + + + + + diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/Location.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/Location.hbm.xml deleted file mode 100644 index fed531a49fde..000000000000 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/Location.hbm.xml +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - location_location_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/LocationAttributeType.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/LocationAttributeType.hbm.xml deleted file mode 100644 index 106909c45be4..000000000000 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/LocationAttributeType.hbm.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - location_attribute_type_location_attribute_type_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/PatientState.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/PatientState.hbm.xml deleted file mode 100644 index f48e230bdb9d..000000000000 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/PatientState.hbm.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - patient_state_patient_state_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/PersonAddress.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/PersonAddress.hbm.xml deleted file mode 100644 index 6edafc24eebc..000000000000 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/PersonAddress.hbm.xml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - person_address_person_address_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/PersonAttributeType.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/PersonAttributeType.hbm.xml deleted file mode 100644 index a717a10b657d..000000000000 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/PersonAttributeType.hbm.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - person_attribute_type_person_attribute_type_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/Relationship.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/Relationship.hbm.xml deleted file mode 100644 index ef086e561c4c..000000000000 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/Relationship.hbm.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - relationship_relationship_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/SerializedObject.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/SerializedObject.hbm.xml deleted file mode 100644 index 8d36ed52087c..000000000000 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/SerializedObject.hbm.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - serialized_object_serialized_object_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/src/main/resources/org/openmrs/api/db/hibernate/User.hbm.xml b/api/src/main/resources/org/openmrs/api/db/hibernate/User.hbm.xml deleted file mode 100644 index 280c82d0e6fe..000000000000 --- a/api/src/main/resources/org/openmrs/api/db/hibernate/User.hbm.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - users_user_id_seq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/src/main/resources/org/openmrs/liquibase/snapshots/core-data/liquibase-core-data-2.6.x.xml b/api/src/main/resources/org/openmrs/liquibase/snapshots/core-data/liquibase-core-data-2.6.x.xml new file mode 100644 index 000000000000..f5f8da9e7205 --- /dev/null +++ b/api/src/main/resources/org/openmrs/liquibase/snapshots/core-data/liquibase-core-data-2.6.x.xml @@ -0,0 +1,5221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/src/main/resources/org/openmrs/liquibase/snapshots/schema-only/liquibase-schema-only-2.6.x.xml b/api/src/main/resources/org/openmrs/liquibase/snapshots/schema-only/liquibase-schema-only-2.6.x.xml new file mode 100644 index 000000000000..9370739daac7 --- /dev/null +++ b/api/src/main/resources/org/openmrs/liquibase/snapshots/schema-only/liquibase-schema-only-2.6.x.xml @@ -0,0 +1,6925 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.0.x.xml b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.0.x.xml index a668a33be2dc..e319301c2a4a 100644 --- a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.0.x.xml +++ b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.0.x.xml @@ -56,7 +56,7 @@ - + Create the foreign key from the privilege required for to edit @@ -1702,7 +1702,7 @@ - + Dropping foreign key on concept_set.concept_id table @@ -1745,7 +1745,7 @@ - + Dropping foreign key on patient_identifier.patient_id column @@ -2265,7 +2265,7 @@ - + Dropping foreign key on concept_word.concept_id column @@ -2305,7 +2305,7 @@ - + Re-adding foreign key for concept_word.concept_name_id @@ -2314,7 +2314,7 @@ - + Adding foreign key for concept_word.concept_id column @@ -2527,7 +2527,7 @@ - + Remove the concept_source.voided_by foreign key constraint @@ -2727,7 +2727,7 @@ - + @@ -2771,7 +2771,7 @@ - + Restoring foreign key constraint on users.person_id @@ -2951,7 +2951,7 @@ - + Create the foreign key from the relationship.retired_by to users.user_id. @@ -3026,7 +3026,7 @@ - + Dropping unused foreign key from notification alert table @@ -3490,7 +3490,7 @@ - + Dropping foreign key constraint on concept_name_tag_map.concept_name_tag_id @@ -3613,7 +3613,7 @@ - + Restoring foreign key constraint on concept_name_tag_map.concept_name_tag_id @@ -4131,7 +4131,7 @@ - @@ -5384,7 +5384,7 @@ - + Adding foreign key constraint to concept_reference_map.concept_reference_term_id column - + Dropping foreign key constraint on concept_reference_map.source column @@ -5426,7 +5426,7 @@ - + Remove ON DELETE CASCADE from relationship table for person_a @@ -5436,7 +5436,7 @@ - + Remove ON DELETE CASCADE from relationship table for person_b @@ -6433,7 +6433,7 @@ - @@ -6446,7 +6446,7 @@ - @@ -6459,7 +6459,7 @@ - @@ -7143,7 +7143,7 @@ - + Adding FK constraint for test_order.specimen_source if necessary - + Adding quantity_units column to drug_order table @@ -7875,7 +7875,7 @@ - + Adding foreignKey constraint on dose_units - + Dropping fk constraint on orders.discontinued_by column to users.user_id column @@ -8011,7 +8011,7 @@ - + Adding the frequency column to the drug_order table @@ -8163,7 +8163,7 @@ - + Add foreign key constraint - + Temporary dropping foreign key on orders.discontinued_reason column @@ -8253,7 +8253,7 @@ - + Adding back foreign key on orders.discontinued_reason column - + Temporarily removing foreign key constraint from orders.orderer column @@ -8286,7 +8286,7 @@ - + Adding foreign key constraint to orders.orderer column - + Removing invalid foreign key constraint from order_type.parent column to order.order_id column @@ -8505,7 +8505,7 @@ - + Adding foreign key constraint from order_type.parent column to order_type.order_type_id column @@ -8528,7 +8528,7 @@ - + Dropping foreign key on patient.tribe @@ -8633,7 +8633,7 @@ - + Temporarily removing foreign key constraint from person_attribute_type.edit_privilege column @@ -8641,7 +8641,7 @@ - + Temporarily removing foreign key constraint from role_privilege.privilege column @@ -8661,7 +8661,7 @@ - + Adding foreign key constraint to person_attribute_type.edit_privilege column - + Adding foreign key constraint to role_privilege.privilege column - + Adding foreign key on patient_identifier.patient_id column diff --git a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.1.x.xml b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.1.x.xml index f3641be35647..784f7531aa1d 100644 --- a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.1.x.xml +++ b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.1.x.xml @@ -42,14 +42,14 @@ - + Dropping foreign key constraint member_patient - + Dropping foreign key constraint parent_cohort diff --git a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.4.x.xml b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.4.x.xml index 71b1bd3269e0..7ef379c4b752 100644 --- a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.4.x.xml +++ b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.4.x.xml @@ -116,7 +116,7 @@ - + Updating foreign key concept_attributes to add delete CASCADES @@ -125,7 +125,7 @@ - + Updating foreign key numeric_attributes to add delete CASCADES @@ -133,7 +133,7 @@ - + Updating foreign key person_id_for_patient to add delete CASCADES @@ -141,7 +141,7 @@ - + Updating foreign key test_order_order_id_fk to add delete CASCADES diff --git a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.5.x.xml b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.5.x.xml index 96c7bb63d7f9..1cc135ca361e 100644 --- a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.5.x.xml +++ b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.5.x.xml @@ -366,7 +366,7 @@ - + Updating foreign key user_who_changed_user to add delete CASCADE diff --git a/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.7.x.xml b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.7.x.xml new file mode 100644 index 000000000000..e505ca0f778a --- /dev/null +++ b/api/src/main/resources/org/openmrs/liquibase/updates/liquibase-update-to-latest-2.7.x.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + Adding unique index on location_tag name column + + + + + + + + + + + + Adding optional property 'view_privilege' to 'global_property' table + + + + + + + + + + + + + + + + Adding optional property 'edit_privilege' to 'global_property' table + + + + + + + + + + + + + + + + Adding optional property 'delete_privilege' to 'global_property' table + + + + + + + + + + Soundex extension for PostgreSQL + CREATE EXTENSION IF NOT EXISTS fuzzystrmatch SCHEMA public; + + + + Extension to use UUID functions with PostgreSQL and creating an alias similar to MySQL + + CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA public; + CREATE FUNCTION UUID() RETURNS UUID LANGUAGE SQL AS $$ SELECT uuid_generate_v1() $$; + + + + diff --git a/api/src/main/resources/org/openmrs/module/dtd/README.md b/api/src/main/resources/org/openmrs/module/dtd/README.md index 1eba2e830c6c..7ef74754f2c4 100644 --- a/api/src/main/resources/org/openmrs/module/dtd/README.md +++ b/api/src/main/resources/org/openmrs/module/dtd/README.md @@ -13,7 +13,7 @@ your config. Like so ```xml + "https://resources.openmrs.org/doctype/config-1.5.dtd"> ... diff --git a/api/src/main/resources/org/openmrs/module/dtd/config-1.5.dtd b/api/src/main/resources/org/openmrs/module/dtd/config-1.5.dtd index 6fafe00f93eb..ff3777b2efc2 100644 --- a/api/src/main/resources/org/openmrs/module/dtd/config-1.5.dtd +++ b/api/src/main/resources/org/openmrs/module/dtd/config-1.5.dtd @@ -27,7 +27,7 @@ (filter*), (filter-mapping*), (messages*), - (mappingFiles?) + (mappingFiles?), (packagesWithMappedClasses?) )> diff --git a/api/src/main/resources/org/openmrs/module/dtd/config-1.6.dtd b/api/src/main/resources/org/openmrs/module/dtd/config-1.6.dtd index 1af510a4e688..199805acccf8 100644 --- a/api/src/main/resources/org/openmrs/module/dtd/config-1.6.dtd +++ b/api/src/main/resources/org/openmrs/module/dtd/config-1.6.dtd @@ -15,6 +15,7 @@ (require_database_version?), (require_modules?), (aware_of_modules?), + (start_before_modules?), (mandatory?), (library*), (extension*), @@ -26,8 +27,8 @@ (filter*), (filter-mapping*), (messages*), - (mappingFiles?) - (packagesWithMappedClasses?) + (mappingFiles?), + (packagesWithMappedClasses?), (conditionalResources?) )> @@ -51,6 +52,10 @@ + + + + diff --git a/api/src/main/resources/org/openmrs/module/dtd/config-1.7.dtd b/api/src/main/resources/org/openmrs/module/dtd/config-1.7.dtd new file mode 100644 index 000000000000..7226bc82ae77 --- /dev/null +++ b/api/src/main/resources/org/openmrs/module/dtd/config-1.7.dtd @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/src/test/java/org/openmrs/ConditionTest.java b/api/src/test/java/org/openmrs/ConditionTest.java new file mode 100644 index 000000000000..6476fac033a8 --- /dev/null +++ b/api/src/test/java/org/openmrs/ConditionTest.java @@ -0,0 +1,184 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests of methods within the Condition class + * @see Condition + */ +public class ConditionTest { + + Condition baseCondition = new Condition(); + Concept concept1 = new Concept(1); + Concept concept2 = new Concept(2); + ConceptName conceptName1 = new ConceptName(1); + ConceptName conceptName2 = new ConceptName(2); + String text1 = "Text 1"; + String text2 = "Text 2"; + DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + + @BeforeEach + public void before() throws Exception { + baseCondition.setConditionId(1234); + baseCondition.setUuid(UUID.randomUUID().toString()); + baseCondition.setCondition(new CodedOrFreeText(concept1, conceptName1, text1)); + baseCondition.setPreviousVersion(null); + baseCondition.setPatient(null); + baseCondition.setEncounter(null); + baseCondition.setAdditionalDetail(text1); + baseCondition.setFormNamespaceAndPath(text1); + baseCondition.setOnsetDate(df.parse("2022-03-22")); + baseCondition.setEndDate(df.parse("2023-01-19")); + baseCondition.setEndReason(text1); + baseCondition.setClinicalStatus(ConditionClinicalStatus.ACTIVE); + baseCondition.setVerificationStatus(ConditionVerificationStatus.PROVISIONAL); + baseCondition.setVoided(false); + baseCondition.setVoidReason(null); + baseCondition.setDateVoided(null); + baseCondition.setVoidedBy(null); + } + + @Test + public void matches_shouldReturnFalseIfNoFieldsHaveChanged() { + Condition condition = Condition.newInstance(baseCondition); + assertTrue(condition.matches(baseCondition)); + assertTrue(baseCondition.matches(condition)); + } + + @Test + public void matches_shouldReturnFalseIfOnlyIdentityFieldsHaveChanged() { + Condition condition = Condition.newInstance(baseCondition); + condition.setId(5678); + assertTrue(condition.matches(baseCondition)); + assertTrue(baseCondition.matches(condition)); + condition.setUuid(UUID.randomUUID().toString()); + assertTrue(condition.matches(baseCondition)); + assertTrue(baseCondition.matches(condition)); + } + + @Test + public void matches_shouldReturnTrueIfNonIdentityFieldsHaveChanged() { + Condition condition = Condition.newInstance(baseCondition); + assertTrue(condition.matches(baseCondition)); + + // Condition Coded + condition.setCondition(new CodedOrFreeText(concept2, conceptName1, text1)); + assertFalse(condition.matches(baseCondition)); + condition.setCondition(baseCondition.getCondition()); + assertTrue(condition.matches(baseCondition)); + + // Condition Concept Name + condition.setCondition(new CodedOrFreeText(concept1, conceptName2, text1)); + assertFalse(condition.matches(baseCondition)); + condition.setCondition(baseCondition.getCondition()); + assertTrue(condition.matches(baseCondition)); + + // Condition Non-Coded + condition.setCondition(new CodedOrFreeText(concept1, conceptName1, text2)); + assertFalse(condition.matches(baseCondition)); + condition.setCondition(baseCondition.getCondition()); + assertTrue(condition.matches(baseCondition)); + + // Previous version + condition.setPreviousVersion(new Condition()); + assertFalse(condition.matches(baseCondition)); + condition.setPreviousVersion(baseCondition.getPreviousVersion()); + assertTrue(condition.matches(baseCondition)); + + // Patient + condition.setPatient(new Patient()); + assertFalse(condition.matches(baseCondition)); + condition.setPatient(baseCondition.getPatient()); + assertTrue(condition.matches(baseCondition)); + + // Encounter + condition.setEncounter(new Encounter()); + assertFalse(condition.matches(baseCondition)); + condition.setEncounter(baseCondition.getEncounter()); + assertTrue(condition.matches(baseCondition)); + + // Additional details + condition.setAdditionalDetail(text2); + assertFalse(condition.matches(baseCondition)); + condition.setAdditionalDetail(baseCondition.getAdditionalDetail()); + assertTrue(condition.matches(baseCondition)); + + // Form namespace and path + condition.setFormNamespaceAndPath(text2); + assertFalse(condition.matches(baseCondition)); + condition.setFormNamespaceAndPath(baseCondition.getFormNamespaceAndPath()); + assertTrue(condition.matches(baseCondition)); + + // Onset date + condition.setOnsetDate(new Date()); + assertFalse(condition.matches(baseCondition)); + condition.setOnsetDate(baseCondition.getOnsetDate()); + assertTrue(condition.matches(baseCondition)); + + // Onset date + condition.setEndDate(new Date()); + assertFalse(condition.matches(baseCondition)); + condition.setEndDate(baseCondition.getEndDate()); + assertTrue(condition.matches(baseCondition)); + + // End reason + condition.setEndReason(text2); + assertFalse(condition.matches(baseCondition)); + condition.setEndReason(baseCondition.getEndReason()); + assertTrue(condition.matches(baseCondition)); + + // Clinical Status + condition.setClinicalStatus(ConditionClinicalStatus.INACTIVE); + assertFalse(condition.matches(baseCondition)); + condition.setClinicalStatus(baseCondition.getClinicalStatus()); + assertTrue(condition.matches(baseCondition)); + + // Verification Status + condition.setVerificationStatus(ConditionVerificationStatus.CONFIRMED); + assertFalse(condition.matches(baseCondition)); + condition.setVerificationStatus(baseCondition.getVerificationStatus()); + assertTrue(condition.matches(baseCondition)); + + // Voided + condition.setVoided(true); + assertFalse(condition.matches(baseCondition)); + condition.setVoided(baseCondition.getVoided()); + assertTrue(condition.matches(baseCondition)); + + // Void reason + condition.setVoidReason(text2); + assertFalse(condition.matches(baseCondition)); + condition.setVoidReason(baseCondition.getVoidReason()); + assertTrue(condition.matches(baseCondition)); + + // Date voided + condition.setDateVoided(new Date()); + assertFalse(condition.matches(baseCondition)); + condition.setDateVoided(baseCondition.getDateVoided()); + assertTrue(condition.matches(baseCondition)); + + // Voided by + condition.setVoidedBy(new User()); + assertFalse(condition.matches(baseCondition)); + condition.setVoidedBy(baseCondition.getVoidedBy()); + assertTrue(condition.matches(baseCondition)); + } +} diff --git a/api/src/test/java/org/openmrs/OpenmrsTestsTest.java b/api/src/test/java/org/openmrs/OpenmrsTestsTest.java index 05432bef44f7..bd208ec5b75c 100644 --- a/api/src/test/java/org/openmrs/OpenmrsTestsTest.java +++ b/api/src/test/java/org/openmrs/OpenmrsTestsTest.java @@ -29,6 +29,7 @@ import org.junit.Ignore; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; import org.openmrs.annotation.OpenmrsProfileExcludeFilterWithModulesJUnit4Test; import org.openmrs.annotation.StartModuleAnnotationJUnit4Test; import org.openmrs.annotation.StartModuleAnnotationReuseJUnit4Test; @@ -98,7 +99,7 @@ public void shouldHaveTestAnnotationWhenStartingWithShould() { // make sure every should___ method has an @Test annotation if (methodName.startsWith("should") || methodName.contains("_should")) { - assertTrue(method.getAnnotation(Test.class) != null || method.getAnnotation(org.junit.Test.class) != null, currentClass.getName() + "#" + methodName + " does not have the @Test annotation on it even though the method name starts with 'should'"); + assertTrue(method.getAnnotation(Test.class) != null || method.getAnnotation(org.junit.Test.class) != null || method.getAnnotation(ParameterizedTest.class) != null, currentClass.getName() + "#" + methodName + " does not have the @Test annotation on it even though the method name starts with 'should'"); } } } diff --git a/api/src/test/java/org/openmrs/OrderTest.java b/api/src/test/java/org/openmrs/OrderTest.java index 310382c392fa..80ed0c0863c2 100644 --- a/api/src/test/java/org/openmrs/OrderTest.java +++ b/api/src/test/java/org/openmrs/OrderTest.java @@ -94,6 +94,7 @@ protected static void assertThatAllFieldsAreCopied(Order original, String method Order copy = (Order) MethodUtils.invokeExactMethod(original, methodName, null); for (Field field : fields) { + field.setAccessible(true); Object copyValue = field.get(copy); if (fieldsToExclude.contains(field.getName())) { continue; diff --git a/api/src/test/java/org/openmrs/aop/RequiredDataAdviceTest.java b/api/src/test/java/org/openmrs/aop/RequiredDataAdviceTest.java index e1774d4b9323..ca306b8b34b3 100644 --- a/api/src/test/java/org/openmrs/aop/RequiredDataAdviceTest.java +++ b/api/src/test/java/org/openmrs/aop/RequiredDataAdviceTest.java @@ -13,8 +13,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,7 +36,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.Spy; import org.openmrs.BaseOpenmrsData; @@ -493,7 +493,7 @@ public void before_shouldNotCallHandlerOnSaveWithNullOrNoArguments() throws Thro SomeOpenmrsData openmrsObject = new SomeOpenmrsData(); requiredDataAdvice.before(m, null, new WithAppropriatelyNamedMethod()); requiredDataAdvice.before(m, new Object[] {}, new WithAppropriatelyNamedMethod()); - verify(saveHandler, never()).handle(eq(openmrsObject), Matchers.anyObject(), Matchers.anyObject(), + verify(saveHandler, never()).handle(eq(openmrsObject), any(), any(), anyString()); } @@ -507,8 +507,8 @@ public void before_shouldCallHandlerOnSaveWithOpenmrsObjectArgument() throws Thr Method m = WithAppropriatelyNamedMethod.class.getMethod("saveSomeOpenmrsData", SomeOpenmrsData.class); SomeOpenmrsData openmrsObject = new SomeOpenmrsData(); requiredDataAdvice.before(m, new Object[] { openmrsObject }, new WithAppropriatelyNamedMethod()); - verify(saveHandler, times(1)).handle(eq(openmrsObject), Matchers.anyObject(), Matchers.anyObject(), - Matchers.any()); + verify(saveHandler, times(1)).handle(eq(openmrsObject), any(), any(), + any()); } @Test @@ -517,7 +517,7 @@ public void before_shouldNotCallHandlerOnSaveMethodNameNotMatchingDomainObject() Method m = WithAppropriatelyNamedMethod.class.getMethod("saveSomeOpenmrsDataButNotReally", SomeOpenmrsData.class); SomeOpenmrsData openmrsObject = new SomeOpenmrsData(); requiredDataAdvice.before(m, new Object[] { openmrsObject }, new WithAppropriatelyNamedMethod()); - verify(saveHandler, never()).handle(eq(openmrsObject), Matchers.anyObject(), Matchers.anyObject(), + verify(saveHandler, never()).handle(eq(openmrsObject), any(), any(), anyString()); } @@ -531,8 +531,8 @@ public void before_shouldCallHandlerOnSaveMethodNameWithCollectionArgument() thr Method m = WithAppropriatelyNamedMethod.class.getMethod("saveSomeOpenmrsDatas", List.class); List openmrsObjects = Arrays.asList(new SomeOpenmrsData(), new SomeOpenmrsData()); requiredDataAdvice.before(m, new Object[] { openmrsObjects }, new WithAppropriatelyNamedMethod()); - verify(saveHandler, times(2)).handle(Matchers.anyObject(), Matchers.anyObject(), - Matchers.anyObject(), Matchers.any()); + verify(saveHandler, times(2)).handle(any(), any(), + any(), any()); } @Test @@ -542,7 +542,7 @@ public void before_shouldNotCallHandlerOnVoidWithNullOrNoArguments() throws Thro SomeOpenmrsData openmrsObject = new SomeOpenmrsData(); requiredDataAdvice.before(m, null, new WithAppropriatelyNamedMethod()); requiredDataAdvice.before(m, new Object[] {}, new WithAppropriatelyNamedMethod()); - verify(voidHandler, never()).handle(eq(openmrsObject), Matchers.anyObject(), Matchers.anyObject(), + verify(voidHandler, never()).handle(eq(openmrsObject), any(), any(), anyString()); } @@ -556,7 +556,7 @@ public void before_shouldCallHandlerOnVoidMethodNameMatchingDomainObject() throw Method m = WithAppropriatelyNamedMethod.class.getMethod("voidSomeOpenmrsData", SomeOpenmrsData.class); SomeOpenmrsData openmrsObject = new SomeOpenmrsData(); requiredDataAdvice.before(m, new Object[] { openmrsObject, "void reason" }, new WithAppropriatelyNamedMethod()); - verify(voidHandler, times(1)).handle(eq(openmrsObject), Matchers.anyObject(), Matchers.anyObject(), + verify(voidHandler, times(1)).handle(eq(openmrsObject), any(), any(), anyString()); } @@ -571,8 +571,8 @@ public void before_shouldCallHandlerOnVoidMethodWhenDomainObjectIsAssignableFrom SomeOpenmrsData openmrsObjectSubClass = new SomeOpenmrsDataSubClass(); requiredDataAdvice.before(m, new Object[] { openmrsObjectSubClass, "void reason" }, new WithAppropriatelyNamedMethod()); - verify(voidHandler, times(1)).handle(eq(openmrsObjectSubClass), Matchers.anyObject(), - Matchers.anyObject(), anyString()); + verify(voidHandler, times(1)).handle(eq(openmrsObjectSubClass), any(), + any(), anyString()); } @Test @@ -581,7 +581,7 @@ public void before_shouldNotCallHandlerOnVoidMethodNameNotMatchingDomainObject() Method m = WithAppropriatelyNamedMethod.class.getMethod("voidSomeOpenmrsDataButNotReally", SomeOpenmrsData.class); SomeOpenmrsData openmrsObject = new SomeOpenmrsData(); requiredDataAdvice.before(m, new Object[] { openmrsObject }, new WithAppropriatelyNamedMethod()); - verify(voidHandler, never()).handle(eq(openmrsObject), Matchers.anyObject(), Matchers.anyObject(), + verify(voidHandler, never()).handle(eq(openmrsObject), any(), any(), anyString()); } @@ -606,7 +606,7 @@ public void before_shouldNotCallHandlersAnnotatedAsDisabled() throws Throwable { requiredDataAdvice.before(m, new Object[] { openmrsObject, "void reason" }, new WithAppropriatelyNamedMethod()); // verify that the handle method was never called on this object - verify(voidHandler, never()).handle(eq(person), Matchers.anyObject(), Matchers.anyObject(), + verify(voidHandler, never()).handle(eq(person), any(), any(), anyString()); } @@ -632,7 +632,7 @@ public void before_shouldCallHandlersNotAnnotatedAsDisabled() throws Throwable { requiredDataAdvice.before(m, new Object[] { openmrsObject, "void reason" }, new WithAppropriatelyNamedMethod()); // verify that the handle method was called on this object - verify(voidHandler, times(1)).handle(eq(person), Matchers.anyObject(), Matchers.anyObject(), + verify(voidHandler, times(1)).handle(eq(person), any(), any(), anyString()); } diff --git a/api/src/test/java/org/openmrs/api/AdministrationServiceTest.java b/api/src/test/java/org/openmrs/api/AdministrationServiceTest.java index eaf19ae5de1a..27c59f3f63ac 100644 --- a/api/src/test/java/org/openmrs/api/AdministrationServiceTest.java +++ b/api/src/test/java/org/openmrs/api/AdministrationServiceTest.java @@ -35,8 +35,13 @@ import org.mockito.Mockito; import org.openmrs.GlobalProperty; import org.openmrs.ImplementationId; +import org.openmrs.Privilege; +import org.openmrs.Role; import org.openmrs.User; import org.openmrs.api.context.Context; +import org.openmrs.api.context.Credentials; +import org.openmrs.api.context.UserContext; +import org.openmrs.api.context.UsernamePasswordCredentials; import org.openmrs.customdatatype.datatype.BooleanDatatype; import org.openmrs.customdatatype.datatype.DateDatatype; import org.openmrs.messagesource.MutableMessageSource; @@ -45,6 +50,7 @@ import org.openmrs.util.HttpClient; import org.openmrs.util.LocaleUtility; import org.openmrs.util.OpenmrsConstants; +import org.openmrs.util.PrivilegeConstants; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.SimpleKeyGenerator; @@ -291,6 +297,57 @@ public void getGlobalPropertiesByPrefix_shouldReturnAllRelevantGlobalPropertiesI assertTrue(property.getPropertyValue().startsWith("correct-value")); } } + + @Test + public void getGlobalPropertiesByInvalidPrefix_shouldReturnEmptyList() { + executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml"); + + String invalidPrefix = "non.existing.prefix."; + List properties = adminService.getGlobalPropertiesByPrefix(invalidPrefix); + + assertTrue(properties.isEmpty()); + } + + @Test + public void getGlobalPropertiesByPrefix_shouldReturnEmptyWhenPrefixIsNull() { + executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml"); + List properties = adminService.getGlobalPropertiesByPrefix(null); + + assertNotNull(properties); + assertTrue(properties.isEmpty()); + } + + @Test + public void getGlobalPropertiesBySuffix_shouldReturnAllRelevantGlobalPropertiesInTheDatabase() { + executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml"); + + List properties = adminService.getGlobalPropertiesBySuffix(".abcd"); + + assertNotNull(properties); + assertTrue(properties.size() > 0); + for (GlobalProperty property : properties) { + assertTrue(property.getProperty().endsWith(".abcd")); + } + } + + @Test + public void getGlobalPropertiesByInvalidSuffix_shouldReturnEmptyList() { + executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml"); + + String invalidSuffix = "non.existing.suffix."; + List properties = adminService.getGlobalPropertiesBySuffix(invalidSuffix); + + assertTrue(properties.isEmpty()); + } + + @Test + public void getGlobalPropertiesBySuffix_shouldReturnEmptyWhenSuffixIsNull() { + executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml"); + List properties = adminService.getGlobalPropertiesBySuffix(null); + + assertNotNull(properties); + assertTrue(properties.isEmpty()); + } @Test public void getAllowedLocales_shouldNotFailIfNotGlobalPropertyForLocalesAllowedDefinedYet() { @@ -349,7 +406,7 @@ public void getAllGlobalProperties_shouldReturnAllGlobalPropertiesInTheDatabase( executeDataSet(ADMIN_INITIAL_DATA_XML); assertEquals(allGlobalPropertiesSize + 9, adminService.getAllGlobalProperties().size()); } - + @Test public void getAllowedLocales_shouldReturnAtLeastOneLocaleIfNoLocalesDefinedInDatabaseYet() { assertTrue(adminService.getAllowedLocales().size() > 0); @@ -488,6 +545,247 @@ public void getGlobalProperty_shouldGetPropertyInCaseInsensitiveWay() { assertEquals(orig, noprop); } + @Test + public void filterGlobalPropertiesByViewPrivilege_shouldFilterGlobalPropertiesIfUserIsNotAllowedToViewSomeGlobalProperties() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + + final int originalSize = adminService.getAllGlobalProperties().size(); + // create a new test global property and add view privileges + GlobalProperty property = new GlobalProperty(); + property.setProperty("test_property"); + property.setPropertyValue("test_property_value"); + property.setViewPrivilege(Context.getUserService().getPrivilege("Some Privilege For View Global Properties")); + adminService.saveGlobalProperty(property); + // assert new test global property is saved properly + List properties = adminService.getAllGlobalProperties(); + assertEquals(originalSize + 1, properties.size()); + + // authenticate new user to test view privilege + Context.logout(); + Context.authenticate(getTestUserCredentials()); + // have to add privilege in order to be able to call getAllGlobalProperties() method for new user + Context.addProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES); + + properties = adminService.getAllGlobalProperties(); + int actualSize = properties.size(); + + Context.removeProxyPrivilege(PrivilegeConstants.GET_GLOBAL_PROPERTIES); + Context.logout(); + + assertEquals(actualSize, originalSize); + assertTrue(!properties.contains(property)); + } + + /** + * @see org.openmrs.api.AdministrationService#getGlobalProperty(java.lang.String) + */ + @Test + public void getGlobalProperty_shouldFailIfUserHasNoPrivileges() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + GlobalProperty property = getGlobalPropertyWithViewPrivilege(); + + // authenticate new user without privileges + Context.logout(); + Context.authenticate(getTestUserCredentials()); + + APIException exception = assertThrows(APIException.class, () -> adminService.getGlobalProperty(property.getProperty())); + assertEquals(exception.getMessage(), String.format("Privilege: %s, required to view globalProperty: %s", + property.getViewPrivilege(), property.getProperty())); + } + + /** + * @see org.openmrs.api.AdministrationService#getGlobalProperty(java.lang.String) + */ + @Test + public void getGlobalProperty_shouldReturnGlobalPropertyIfUserIsAllowedToView() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + GlobalProperty property = getGlobalPropertyWithViewPrivilege(); + + // authenticate new user without privileges + Context.logout(); + Context.authenticate(getTestUserCredentials()); + // add required privilege to user + Role role = Context.getUserService().getRole("Provider"); + role.addPrivilege(property.getViewPrivilege()); + Context.getAuthenticatedUser().addRole(role); + + assertNotNull(adminService.getGlobalProperty(property.getProperty())); + } + + /** + * @see org.openmrs.api.AdministrationService#getGlobalPropertyObject(java.lang.String) + */ + @Test + public void getGlobalPropertyObject_shouldFailIfUserHasNoPrivileges() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + GlobalProperty property = getGlobalPropertyWithViewPrivilege(); + + // authenticate new user without privileges + Context.logout(); + Context.authenticate(getTestUserCredentials()); + + APIException exception = assertThrows(APIException.class, () -> adminService.getGlobalPropertyObject(property.getProperty())); + assertEquals(exception.getMessage(), String.format("Privilege: %s, required to view globalProperty: %s", + property.getViewPrivilege(), property.getProperty())); + } + + /** + * @see org.openmrs.api.AdministrationService#getGlobalPropertyObject(java.lang.String) + */ + @Test + public void getGlobalPropertyObject_shouldReturnGlobalPropertyIfUserIsAllowedToView() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + GlobalProperty property = getGlobalPropertyWithViewPrivilege(); + + // authenticate new user without privileges + Context.logout(); + Context.authenticate(getTestUserCredentials()); + // add required privilege to user + Role role = Context.getUserService().getRole("Provider"); + role.addPrivilege(property.getViewPrivilege()); + Context.getAuthenticatedUser().addRole(role); + + assertNotNull(adminService.getGlobalPropertyObject(property.getProperty())); + } + + /** + * @see org.openmrs.api.AdministrationService#updateGlobalProperty(java.lang.String, java.lang.String) + */ + @Test + public void updateGlobalProperty_shouldFailIfUserIsNotAllowedToEditGlobalProperty() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + GlobalProperty property = getGlobalPropertyWithEditPrivilege(); + assertEquals("anothervalue", property.getPropertyValue()); + + // authenticate new user without privileges + Context.logout(); + Context.authenticate(getTestUserCredentials()); + + APIException exception = assertThrows(APIException.class, () -> adminService.updateGlobalProperty(property.getProperty(), "new-value")); + assertEquals(exception.getMessage(), String.format("Privilege: %s, required to edit globalProperty: %s", + property.getEditPrivilege(), property.getProperty())); + } + + /** + * @see org.openmrs.api.AdministrationService#updateGlobalProperty(java.lang.String, java.lang.String) + */ + @Test + public void updateGlobalProperty_shouldUpdateIfUserIsAllowedToEditGlobalProperty() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + GlobalProperty property = getGlobalPropertyWithEditPrivilege(); + assertEquals("anothervalue", property.getPropertyValue()); + + // authenticate new user without privileges + Context.logout(); + Context.authenticate(getTestUserCredentials()); + // add required privilege to user + Role role = Context.getUserService().getRole("Provider"); + role.addPrivilege(property.getEditPrivilege()); + Context.getAuthenticatedUser().addRole(role); + + adminService.updateGlobalProperty(property.getProperty(), "new-value"); + String newValue = adminService.getGlobalProperty(property.getProperty()); + assertEquals("new-value", newValue); + } + + /** + * @see org.openmrs.api.AdministrationService#saveGlobalProperty(org.openmrs.GlobalProperty) + */ + @Test + public void saveGlobalProperty_shouldFailIfUserIsNotSupposedToEditGlobalProperty() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + GlobalProperty property = getGlobalPropertyWithEditPrivilege(); + + // authenticate new user without privileges + Context.logout(); + Context.authenticate(getTestUserCredentials()); + // have to add privilege in order to be able to call saveGlobalProperty(GlobalProperty) method + Context.addProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES); + + APIException exception = assertThrows(APIException.class, () -> adminService.saveGlobalProperty(property)); + assertEquals(exception.getMessage(), String.format("Privilege: %s, required to edit globalProperty: %s", + property.getEditPrivilege(), property.getProperty())); + } + + /** + * @see org.openmrs.api.AdministrationService#purgeGlobalProperty(org.openmrs.GlobalProperty) + */ + @Test + public void purgeGlobalProperty_shouldFailIfUserIsNotSupposedToDeleteGlobalProperty() { + executeDataSet(ADMIN_INITIAL_DATA_XML); + GlobalProperty property = getGlobalPropertyWithDeletePrivilege(); + + // authenticate new user without privileges + Context.logout(); + Context.authenticate(getTestUserCredentials()); + // have to add privilege in order to be able to call purgeGlobalProperty(GlobalProperty) method + Context.addProxyPrivilege(PrivilegeConstants.PURGE_GLOBAL_PROPERTIES); + + APIException exception = assertThrows(APIException.class, () -> adminService.purgeGlobalProperty(property)); + assertEquals(exception.getMessage(), String.format("Privilege: %s, required to purge globalProperty: %s", + property.getDeletePrivilege(), property.getProperty())); + } + + /** + * Gets global property and adds view privilege to it + * + * @return global property having non-null view privilege + */ + private GlobalProperty getGlobalPropertyWithViewPrivilege() { + GlobalProperty property = adminService.getGlobalPropertyObject("another-global-property"); + assertNotNull(property); + + Privilege viewPrivilege = Context.getUserService().getPrivilege("Some Privilege For View Global Properties"); + property.setViewPrivilege(viewPrivilege); + property = adminService.saveGlobalProperty(property); + assertNotNull(property.getViewPrivilege()); + + return property; + } + + /** + * Gets global property and adds edit privilege to it + * + * @return global property having non-null edit privilege + */ + private GlobalProperty getGlobalPropertyWithEditPrivilege() { + GlobalProperty property = adminService.getGlobalPropertyObject("another-global-property"); + assertNotNull(property); + + Privilege editPrivilege = Context.getUserService().getPrivilege("Some Privilege For Edit Global Properties"); + property.setEditPrivilege(editPrivilege); + property = adminService.saveGlobalProperty(property); + assertNotNull(property.getEditPrivilege()); + + return property; + } + + /** + * Gets global property and adds delete privilege to it + * + * @return global property having non-null delete privilege + */ + private GlobalProperty getGlobalPropertyWithDeletePrivilege() { + GlobalProperty property = adminService.getGlobalPropertyObject("another-global-property"); + assertNotNull(property); + + Privilege deletePrivilege = Context.getUserService().getPrivilege("Some Privilege For Delete Global Properties"); + property.setDeletePrivilege(deletePrivilege); + property = adminService.saveGlobalProperty(property); + assertNotNull(property.getDeletePrivilege()); + + return property; + } + + /** + * Gets the credentials of the test_user to be authenticated + * + * @return test_user credentials + */ + private Credentials getTestUserCredentials() { + return new UsernamePasswordCredentials("test_user", "test"); + } + @Test public void saveGlobalProperty_shouldNotAllowDifferentPropertiesToHaveTheSameStringWithDifferentCase() { executeDataSet("org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml"); diff --git a/api/src/test/java/org/openmrs/api/AdministrationServiceUnitTest.java b/api/src/test/java/org/openmrs/api/AdministrationServiceUnitTest.java index 857a254108aa..31f971d958c6 100644 --- a/api/src/test/java/org/openmrs/api/AdministrationServiceUnitTest.java +++ b/api/src/test/java/org/openmrs/api/AdministrationServiceUnitTest.java @@ -13,8 +13,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/api/src/test/java/org/openmrs/api/CohortServiceTest.java b/api/src/test/java/org/openmrs/api/CohortServiceTest.java index e8539a395bf9..3cf1d72f430b 100644 --- a/api/src/test/java/org/openmrs/api/CohortServiceTest.java +++ b/api/src/test/java/org/openmrs/api/CohortServiceTest.java @@ -43,6 +43,7 @@ public class CohortServiceTest extends BaseContextSensitiveTest { protected static final String CREATE_PATIENT_XML = "org/openmrs/api/include/PatientServiceTest-createPatient.xml"; protected static final String COHORT_XML = "org/openmrs/api/include/CohortServiceTest-cohort.xml"; + protected static final String COHORT_ORDERING_XML = "org/openmrs/api/include/CohortServiceOrderingTest-cohort.xml"; protected static CohortService service = null; @@ -145,7 +146,51 @@ public void getCohorts_shouldMatchCohortsByPartialName() { matchedCohorts = service.getCohorts("Examples"); assertEquals(0, matchedCohorts.size()); } - + + /** + * @see CohortService#getCohorts(String) + */ + @Test + public void getCohorts_shouldBeCaseInsensitive() { + executeDataSet(COHORT_XML); + + List lowerCaseMatch = service.getCohorts("example"); + List upperCaseMatch = service.getCohorts("EXAMPLE"); + List mixedCaseMatch = service.getCohorts("ExAmPlE"); + + assertNotNull(lowerCaseMatch); + assertNotNull(upperCaseMatch); + assertNotNull(mixedCaseMatch); + + assertEquals(2, lowerCaseMatch.size()); + assertEquals(2, upperCaseMatch.size()); + assertEquals(2, mixedCaseMatch.size()); + + assertTrue(lowerCaseMatch.containsAll(upperCaseMatch) && + upperCaseMatch.containsAll(lowerCaseMatch) && + mixedCaseMatch.containsAll(upperCaseMatch)); + } + + /** + * @see CohortService#getCohorts(String) + */ + @Test + public void getCohorts_shouldReturnCohortsInAscendingOrder() { + executeDataSet(COHORT_ORDERING_XML); + + List cohorts = service.getCohorts("Cohort"); + assertNotNull(cohorts); + assertFalse(cohorts.isEmpty()); + + // validate enough cohorts to check ordering + assertTrue(cohorts.size() > 2); + String previousName = ""; + for (Cohort cohort : cohorts) { + assertTrue(cohort.getName().compareTo(previousName) >= 0); + previousName = cohort.getName(); + } + } + /** * @see CohortService#saveCohort(Cohort) */ @@ -337,7 +382,25 @@ public void getAllCohorts_shouldGetAllNonvoidedCohortsInDatabase() { assertEquals(1, allCohorts.size()); assertFalse(allCohorts.get(0).getVoided()); } - + + @Test + public void getAllCohorts_shouldReturnCohortsInAscendingOrderByName() { + executeDataSet(COHORT_ORDERING_XML); + + List allCohorts = service.getAllCohorts(false); + assertNotNull(allCohorts); + assertFalse(allCohorts.isEmpty()); + + // validate enough cohorts to check ordering + assertTrue(allCohorts.size() > 2); + String previousName = ""; + for (Cohort cohort : allCohorts) { + assertTrue(cohort.getName().compareTo(previousName) >= 0); + previousName = cohort.getName(); + } + } + + /** * @see CohortService#getAllCohorts() */ @@ -434,7 +497,7 @@ public void getCohortsContainingPatient_shouldReturnCohortsThatHaveGivenPatient( List cohortsWithGivenPatient = service.getCohortsContainingPatientId(patientToAdd.getId()); assertTrue(cohortsWithGivenPatient.contains(service.getCohort(2))); } - + /** * @see CohortService#addPatientToCohort(Cohort,Patient) */ @@ -525,7 +588,17 @@ public void voidCohortMembership_shouldVoidCohortMembership() { assertEquals(reason, cm.getVoidReason()); assertFalse(cohort.contains(cm.getPatientId())); } - + + @Test + public void getCohort_shouldGetCohortByName() { + executeDataSet(COHORT_XML); + + Cohort cohort = service.getCohortByName("Example Cohort"); + assertNotNull(cohort); + assertEquals("Example Cohort", cohort.getName()); + assertFalse(cohort.getVoided()); + } + @Test public void endCohortMembership_shouldEndTheCohortMembership() { Date endOnDate = new Date(); @@ -729,4 +802,26 @@ public void getCohortMemberships_shouldNotGetMembershipsContainingPatientOutside List memberships = service.getCohortMemberships(6, longAgo, false); assertThat(memberships.size(), is(0)); } + + @Test + public void getCohortMemberships_shouldIncludeVoidedMembershipsWhenSpecified() throws Exception { + executeDataSet(COHORT_XML); + + // patientId 2 is in a voided cohort (cohortId 1) + List memberships = service.getCohortMemberships(2, null, true); + + assertNotNull(memberships); + assertFalse(memberships.isEmpty()); + + boolean foundVoidedCohortMembership = false; + for (CohortMembership cm : memberships) { + if (cm.getCohort().getCohortId().equals(1)) { + assertTrue(cm.getCohort().getVoided()); + foundVoidedCohortMembership = true; + break; + } + } + + assertTrue(foundVoidedCohortMembership, "Expected to find a membership from a voided cohort"); + } } diff --git a/api/src/test/java/org/openmrs/api/ConceptServiceTest.java b/api/src/test/java/org/openmrs/api/ConceptServiceTest.java index 16b88bdd73c3..7731a9269887 100644 --- a/api/src/test/java/org/openmrs/api/ConceptServiceTest.java +++ b/api/src/test/java/org/openmrs/api/ConceptServiceTest.java @@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; @@ -185,7 +186,201 @@ public void getConceptByName_shouldGetConceptByPartialName() { List firstConceptsByPartialNameList = conceptService.getConceptsByName(partialNameToFetch); assertThat(firstConceptsByPartialNameList, containsInAnyOrder(hasId(1), hasId(2))); } + + /** + * @see ConceptService#getPrevConcept(Concept) + */ + @Test + public void getPrevConcept_shouldReturnPreviousConceptBasedOnConceptId() { + executeDataSet(INITIAL_CONCEPTS_XML); + + Concept currentConcept = conceptService.getConcept(4); + assertNotNull(currentConcept); + + Concept previousConcept = conceptService.getPrevConcept(currentConcept); + + assertNotNull(previousConcept); + assertEquals((Integer)(currentConcept.getConceptId() - 1), previousConcept.getConceptId()); + } + + /** + * @see ConceptService#getPrevConcept(Concept) + */ + @Test + public void getPrevConcept_shouldReturnNullIfNoPrevConceptId() { + executeDataSet(INITIAL_CONCEPTS_XML); + + Concept currentConcept = conceptService.getConcept(1); + assertNotNull(currentConcept); + + Concept previousConcept = conceptService.getPrevConcept(currentConcept); + + assertNull(previousConcept); + } + + /** + * @see ConceptService#getNextConcept(Concept) + */ + @Test + public void getNextConcept_shouldReturnNextConceptBasedOnConceptId() { + executeDataSet(INITIAL_CONCEPTS_XML); + + Concept currentConcept = conceptService.getConcept(3); + assertNotNull(currentConcept); + + Concept nextConcept = conceptService.getNextConcept(currentConcept); + + assertNotNull(nextConcept); + assertEquals((Integer)(currentConcept.getConceptId() + 1), nextConcept.getConceptId()); + } + + /** + * @see ConceptService#getNextConcept(Concept) + */ + @Test + public void getNextConcept_shouldReturnNullIfNoNextConceptId() { + executeDataSet(INITIAL_CONCEPTS_XML); + + // Use the highest concept ID in your dataset + Concept currentConcept = conceptService.getConcept(5497); + assertNotNull(currentConcept); + + Concept nextConcept = conceptService.getNextConcept(currentConcept); + + assertNull(nextConcept); + } + + /** + * @see ConceptService#getAllConceptProposals(boolean) + */ + @Test + public void getAllConceptProposals_whenIncludeCompletedIsFalse_shouldReturnOnlyUncompletedProposals() { + executeDataSet(INITIAL_CONCEPTS_XML); + + List proposals = conceptService.getAllConceptProposals(false); + + ConceptProposal previousProposal = null; + for (ConceptProposal proposal : proposals) { + assertEquals(OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED, proposal.getState()); + + if (previousProposal != null) { + assertTrue(previousProposal.getOriginalText().compareTo(proposal.getOriginalText()) <= 0); + } + previousProposal = proposal; + } + } + + /** + * @see ConceptService#getConceptProposals(String)) + */ + @Test + public void getConceptProposals_shouldReturnProposalsMatchingTextAndUnmappedState() { + executeDataSet(INITIAL_CONCEPTS_XML); + + String searchText = "unmapped concept proposal"; + + List proposals = conceptService.getConceptProposals(searchText); + + assertEquals(1, proposals.size()); + for (ConceptProposal proposal : proposals) { + assertEquals(OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED, proposal.getState()); + assertEquals(searchText, proposal.getOriginalText()); + } + } + + /** + * @see ConceptService#getProposedConcepts(String)) + */ + @Test + public void getProposedConcepts_shouldReturnConceptsMatchingTextAndExcludingUnmappedState() { + executeDataSet(INITIAL_CONCEPTS_XML); + + String searchText = "mapped concept proposal"; + + List allProposals = conceptService.getAllConceptProposals(true); + assertTrue(allProposals.size() > 0); + + List concepts = conceptService.getProposedConcepts(searchText); + assertEquals(1, concepts.size()); + + Concept actualConcept = concepts.get(0); + for (ConceptProposal proposal : allProposals) { + if (proposal.getMappedConcept() == null) { + continue; + } + + if (proposal.getMappedConcept().getConceptId().intValue() == actualConcept.getConceptId().intValue()) { + assertNotNull(proposal.getMappedConcept()); + assertNotEquals(OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED, proposal.getState()); + assertEquals(actualConcept.getConceptId(), proposal.getMappedConcept().getConceptId()); + } + } + } + + /** + * @see ConceptService#getProposedConcepts(String)) + */ + @Test + public void getProposedConcepts_shouldReturnEmptyWhenNoMappedConcepts() { + executeDataSet(INITIAL_CONCEPTS_XML); + + String searchText = "unmapped concept proposal"; + + List concepts = conceptService.getProposedConcepts(searchText); + assertEquals(0, concepts.size()); + } + + /** + * @see ConceptService#getProposedConcepts(String)) + */ + @Test + public void getConceptSetsByConcept_shouldReturnConceptSetsOrderedBySortWeight() { + executeDataSet(INITIAL_CONCEPTS_XML); + + + Concept concept = conceptService.getConceptByUuid("0f97e14e-cdc2-49ac-9255-b5126f8a5147"); + + List conceptSets = conceptService.getConceptSetsByConcept(concept); + assertEquals(3, conceptSets.size()); + + for (int i = 0; i < conceptSets.size() - 1; i++) { + ConceptSet current = conceptSets.get(i); + ConceptSet next = conceptSets.get(i + 1); + + assertNotNull(current.getSortWeight()); + assertNotNull(next.getSortWeight()); + assertTrue(current.getSortWeight() <= next.getSortWeight()); + } + } + /** + * @see ConceptService#getAllConceptProposals(boolean) + */ + @Test + public void getAllConceptProposals_WhenIncludeCompletedIsTrue_shouldReturnAllProposals() { + executeDataSet(INITIAL_CONCEPTS_XML); + + List proposals = conceptService.getAllConceptProposals(true); + + assertFalse(proposals.isEmpty()); + + + boolean foundCompletedProposal = false; + ConceptProposal previousProposal = null; + for (ConceptProposal proposal : proposals) { + if (!proposal.getState().equals(OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED)) { + foundCompletedProposal = true; + } + + if (previousProposal != null) { + assertTrue(previousProposal.getOriginalText().compareTo(proposal.getOriginalText()) <= 0); + } + previousProposal = proposal; + } + assertTrue(foundCompletedProposal, "No completed proposals were returned."); + } + + /** * @see ConceptService#saveConcept(Concept) */ @@ -412,7 +607,68 @@ public void saveConcept_shouldKeepIdForNewConceptIfOneIsSpecified() { concept = Context.getConceptService().saveConcept(concept); assertTrue(concept.getConceptId().equals(conceptId)); } - + + /** + * @see ConceptService#getAllConceptClasses(boolean) + */ + @Test + public void getAllConceptClasses_whenIncludeRetiredIsFalse_shouldNotReturnRetiredConceptClasses() { + boolean includeRetired = false; + + List conceptClasses = conceptService.getAllConceptClasses(includeRetired); + + assertNotNull(conceptClasses); + assertTrue(conceptClasses.size() > 0); + for (ConceptClass conceptClass : conceptClasses) { + assertFalse(conceptClass.isRetired()); + } + } + + /** + * @see ConceptService#getAllConceptDatatypes(boolean) + */ + @Test + public void getAllConceptDatatypes_whenIncludeRetiredIsFalse_shouldNotReturnRetiredConceptDatatypes() { + boolean includeRetired = false; + + List conceptDatatypes = conceptService.getAllConceptDatatypes(includeRetired); + + assertNotNull(conceptDatatypes); + assertTrue(conceptDatatypes.size() > 0); + for (ConceptDatatype conceptDatatype : conceptDatatypes) { + assertFalse(conceptDatatype.isRetired()); + } + } + + /** + * @see ConceptService#getAllConceptClasses(boolean) + */ + @Test + public void getAllConceptClasses_whenIncludeRetiredIsTrue_shouldReturnAllConceptClasses() { + boolean includeRetired = true; + + List conceptClasses = conceptService.getAllConceptClasses(includeRetired); + + assertNotNull(conceptClasses); + assertTrue(conceptClasses.size() > 0); + + boolean foundRetired = false; + boolean foundNonRetired = false; + for (ConceptClass conceptClass : conceptClasses) { + if (conceptClass.isRetired()) { + foundRetired = true; + } else { + foundNonRetired = true; + } + if (foundRetired && foundNonRetired) { + break; + } + } + + assertTrue(foundRetired, "No retired concept classes found."); + assertTrue(foundNonRetired, "No non-retired concept classes found."); + } + /** * @see ConceptService#conceptIterator() */ @@ -2384,7 +2640,7 @@ public void getConceptsByName_shouldReturnConceptsForAllCountriesAndGlobalLangua assertEquals(3, concepts.size()); assertTrue(concepts.containsAll(Arrays.asList(concept1, concept2, concept3))); } - + /** * @see ConceptService#getConceptsByName(String,Locale) */ diff --git a/api/src/test/java/org/openmrs/api/EncounterServiceTest.java b/api/src/test/java/org/openmrs/api/EncounterServiceTest.java index d377dc9da740..ae5967405c87 100644 --- a/api/src/test/java/org/openmrs/api/EncounterServiceTest.java +++ b/api/src/test/java/org/openmrs/api/EncounterServiceTest.java @@ -11,6 +11,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -36,8 +37,11 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.HashSet; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; +import org.junit.Assert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -49,6 +53,8 @@ import org.openmrs.Concept; import org.openmrs.Condition; import org.openmrs.ConditionClinicalStatus; +import org.openmrs.ConditionVerificationStatus; +import org.openmrs.Diagnosis; import org.openmrs.DrugOrder; import org.openmrs.Encounter; import org.openmrs.EncounterRole; @@ -425,6 +431,30 @@ public void saveEncounter_shouldCascadeSaveToContainedConditions() { assertTrue(savedConditions.contains(pregnancy)); assertTrue(savedConditions.contains(edema)); } + + @Test + public void saveEncounter_shouldCascadeSaveToContainedDiagnoses() { + // setup + Encounter encounter = buildEncounter(); + Diagnosis diagnosis1 = new Diagnosis(); + diagnosis1.setDiagnosis(new CodedOrFreeText(null, null, "Fever")); + diagnosis1.setRank(1); + diagnosis1.setCertainty(ConditionVerificationStatus.CONFIRMED); + + Diagnosis diagnosis2 = new Diagnosis(); + diagnosis2.setDiagnosis(new CodedOrFreeText(null, null, "Also fever")); + diagnosis2.setRank(2); + diagnosis2.setCertainty(ConditionVerificationStatus.PROVISIONAL); + + // replay + encounter.getDiagnoses().add(diagnosis1); + encounter.getDiagnoses().add(diagnosis2); + encounter = Context.getEncounterService().saveEncounter(encounter); + + // verify + assertTrue(Context.getDiagnosisService().getDiagnosesByEncounter(encounter, true, true).contains(diagnosis1)); + assertTrue(Context.getDiagnosisService().getDiagnosesByEncounter(encounter, false, false).contains(diagnosis2)); + } private Encounter buildEncounter() { // First, create a new Encounter @@ -886,7 +916,24 @@ public void getEncountersByPatientId_shouldNotGetVoidedEncounters() { List encounters = encounterService.getEncountersByPatientId(3); assertEquals(2, encounters.size()); } - + + /** + * @see EncounterService#getEncountersByPatientId(Integer) + */ + @Test + public void getEncountersByPatientId_shouldReturnEncountersInDescendingOrder() { + EncounterService encounterService = Context.getEncounterService(); + + List encounters = encounterService.getEncountersByPatientId(3); + assertEquals(2, encounters.size()); + + // Ensure list is ordered by encounterDatetime in descending order + for (int i = 0; i < encounters.size() - 1; i++) { + assertTrue(encounters.get(i).getEncounterDatetime() + .compareTo(encounters.get(i + 1).getEncounterDatetime()) >= 0); + } + } + /** * @see EncounterService#getEncountersByPatientId(Integer) */ @@ -974,7 +1021,33 @@ public void voidEncounter_shouldCascadeToOrders() { assertTrue(order.getVoided()); assertEquals("Just Testing", order.getVoidReason()); } + + /** + * @see EncounterService#voidEncounter(Encounter,String) + */ + @Test + public void voidEncounter_shouldCascadeVoidToDiagnoses() { + EncounterService encounterService = Context.getEncounterService(); + + // get a nonvoided encounter that has some Diagnoses + Encounter encounter = encounterService.getEncounter(1); + // Test that diagnoses is unvoided + Diagnosis unvoidedDiagnosis = encounter.getDiagnoses().iterator().next(); + assertEquals(unvoidedDiagnosis.getDiagnosisId(), 1); + assertFalse(unvoidedDiagnosis.getVoided()); + assertFalse(encounter.getVoided()); + + // Run test + encounterService.voidEncounter(encounter, "Just Testing"); + + // Test that diagnoses is voided + Diagnosis voidedDiagnoses = Context.getDiagnosisService().getDiagnosis(1); + assertTrue(voidedDiagnoses.getVoided()); + assertEquals("Just Testing", voidedDiagnoses.getVoidReason()); + assertTrue(encounter.getVoided()); + } + /** * @see OrderService#voidOrder(org.openmrs.Order, String) */ @@ -1048,6 +1121,32 @@ public void unvoidEncounter_shouldCascadeUnvoidToOrders() { assertNull(order.getVoidReason()); } + /** + * @see EncounterService#unvoidEncounter(Encounter) + */ + @Test + public void unvoidEncounter_shouldCascadeUnvoidToDiagnoses() { + EncounterService encounterService = Context.getEncounterService(); + + // get a voided encounter that has some voided Diagnoses + Encounter encounter = encounterService.getEncounter(2); + + // Test that diagnoses is voided + Diagnosis voidedDiagnosis = encounter.getDiagnoses().iterator().next(); + assertEquals(voidedDiagnosis.getDiagnosisId(), 2); + assertTrue(voidedDiagnosis.getVoided()); + assertTrue(encounter.getVoided()); + + // Run test + encounterService.unvoidEncounter(encounter); + + // Test that diagnoses is unvoided + Diagnosis diagnosis = Context.getDiagnosisService().getDiagnosis(2); + assertFalse(diagnosis.getVoided()); + assertNull(diagnosis.getVoidReason()); + assertFalse(encounter.getVoided()); + } + /** * @see EncounterService#voidEncounter(Encounter,String) */ @@ -3156,4 +3255,89 @@ public void saveEncounter_shouldCascadeSaveToContainedAllergies() { assertTrue(allergies.contains(allergy)); assertEquals(NAMESPACE + "^" + FORMFIELD_PATH, allergies.iterator().next().getFormNamespaceAndPath()); } + + @Test + public void getEncountersByVisitsAndPatient_shouldReturnCorrectEncounters() { + Patient patient = Context.getPatientService().getPatient(3); + + List encounters = Context.getEncounterService() + .getEncountersByVisitsAndPatient(patient, false, null, 0, 5); + + assertEquals(2, encounters.size()); + for(Encounter encounter: encounters) { + assertEquals(patient, encounter.getPatient()); + } + } + + @Test + public void getEncountersByVisitsAndPatient_shouldReturnEncountersWithSpecificVisitType() { + executeDataSet(TRANSFER_ENC_DATA_XML); + + Patient patient = Context.getPatientService().getPatient(200); + + final String QUERY = "Return TB Clinic Visit"; + List encounters = + Context.getEncounterService().getEncountersByVisitsAndPatient(patient, false, QUERY, 0, 5); + + assertEquals(2, encounters.size()); + + for (Encounter encounter : encounters) { + assertEquals(patient, encounter.getPatient()); + assertEquals(QUERY, encounter.getVisit().getVisitType().getName()); + } + } + + @Test + public void getEncountersByVisitsAndPatient_shouldReturnEncountersWithSpecificLocation() { + executeDataSet(TRANSFER_ENC_DATA_XML); + + Patient patient = Context.getPatientService().getPatient(200); + + final String QUERY = "Test Location"; + List encounters = + Context.getEncounterService().getEncountersByVisitsAndPatient(patient, false, QUERY, 0, 5); + + assertEquals(2, encounters.size()); + + for (Encounter encounter : encounters) { + assertEquals(patient, encounter.getPatient()); + assertEquals(QUERY, encounter.getVisit().getLocation().getName()); + } + } + + @Test + public void getEncountersByVisitsAndPatientCount_shouldReturnCorrectEncountersCount() { + Patient patient = Context.getPatientService().getPatient(3); + + Integer count = Context.getEncounterService() + .getEncountersByVisitsAndPatientCount(patient, false, null); + + assertEquals(Integer.valueOf(2), count); + } + + @Test + public void getEncountersByVisitsAndPatientCount_shouldReturnEncountersCountWithSpecificVisitType() { + executeDataSet(TRANSFER_ENC_DATA_XML); + + Patient patient = Context.getPatientService().getPatient(200); + + final String QUERY = "Return TB Clinic Visit"; + Integer count = Context.getEncounterService() + .getEncountersByVisitsAndPatientCount(patient, false, QUERY); + + assertEquals(Integer.valueOf(2), count); + } + + @Test + public void getEncountersByVisitsAndPatientCount_shouldReturnEncountersCountWithSpecificLocation() { + executeDataSet(TRANSFER_ENC_DATA_XML); + + Patient patient = Context.getPatientService().getPatient(200); + + final String QUERY = "Test Location"; + Integer count = Context.getEncounterService() + .getEncountersByVisitsAndPatientCount(patient, false, QUERY); + + assertEquals(Integer.valueOf(2), count); + } } diff --git a/api/src/test/java/org/openmrs/api/FormServiceTest.java b/api/src/test/java/org/openmrs/api/FormServiceTest.java index cd6deb67141b..5422c1654c93 100644 --- a/api/src/test/java/org/openmrs/api/FormServiceTest.java +++ b/api/src/test/java/org/openmrs/api/FormServiceTest.java @@ -29,6 +29,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.collections.ListUtils; import org.junit.jupiter.api.Test; @@ -41,6 +42,7 @@ import org.openmrs.GlobalProperty; import org.openmrs.Obs; import org.openmrs.api.context.Context; +import org.openmrs.api.db.DAOException; import org.openmrs.obs.SerializableComplexObsHandler; import org.openmrs.test.jupiter.BaseContextSensitiveTest; import org.openmrs.util.DateUtil; @@ -238,7 +240,7 @@ public void getFormField_shouldNotFailWithNullIgnoreFormFieldsArgument() { /** * Make sure that multiple forms are returned if a field is on a form more than once * - * @see {@link FormService#getForms(String, Boolean, java.util.Collection, Boolean, java.util.Collection, java.util.Collection, java.util.Collection) + * @see FormService#getForms(String, Boolean, java.util.Collection, Boolean, java.util.Collection, java.util.Collection, java.util.Collection) */ @Test @@ -256,6 +258,144 @@ public void getForms_shouldReturnDuplicateFormWhenGivenFieldsIncludedInFormMulti assertEquals(3, forms.size()); } + /** + * Make sure form is returned when matching against any formFieldId in a list + * + * @see FormService#getForms(String, Boolean, java.util.Collection, Boolean, java.util.Collection, java.util.Collection, java.util.Collection) + + */ + @Test + public void getFormCriteria_shouldReturnFormsWithAnyFormField() throws DAOException { + executeDataSet(INITIAL_FIELDS_XML); + executeDataSet("org/openmrs/api/include/FormServiceTest-formFields.xml"); + + FormService formService = Context.getFormService(); + + List containingAnyFormField = new ArrayList<>(); + FormField formField = new FormField(); + formField.setFormFieldId(2); + containingAnyFormField.add(formField); + + List forms = formService.getForms(null, null, null, null, containingAnyFormField, null, null); + + assertEquals(1, forms.size()); + + Set expectedFormFieldIds = containingAnyFormField.stream() + .map(FormField::getFormFieldId) + .collect(Collectors.toSet()); + + for (Form form : forms) { + Collection formFields = form.getFormFields(); + + assertTrue(formFields.stream().anyMatch(ff -> expectedFormFieldIds.contains(ff.getFormFieldId()))); + } + } + + /** + * Make sure form is returned when matching against all formFieldIds in a list + * + * @see FormService#getForms(String, Boolean, java.util.Collection, Boolean, java.util.Collection, java.util.Collection, java.util.Collection) + + */ + @Test + public void getFormCriteria_shouldReturnFormsWithAllFormFields() throws DAOException { + executeDataSet(INITIAL_FIELDS_XML); + executeDataSet("org/openmrs/api/include/FormServiceTest-formFields.xml"); + + FormService formService = Context.getFormService(); + + List containingAllFormFields = new ArrayList<>(); + FormField formField1 = new FormField(); + formField1.setFormFieldId(2); + containingAllFormFields.add(formField1); + + FormField formField2 = new FormField(); + formField2.setFormFieldId(7); + containingAllFormFields.add(formField2); + + List forms = formService.getForms(null, null, null, null, null, containingAllFormFields, null); + + Set expectedFormFieldIds = containingAllFormFields.stream() + .map(FormField::getFormFieldId) + .collect(Collectors.toSet()); + + assertEquals(1, forms.size()); + + Form form = forms.get(0); + Collection formFields = form.getFormFields(); + Set formFieldIds = formFields.stream().map(FormField::getFormFieldId).collect(Collectors.toSet()); + + assertTrue(formFieldIds.containsAll(expectedFormFieldIds)); + } + + /** + * Make sure form is not returned when matching against all formFieldId in a list where a single formFieldId is not + * present + * + * @see FormService#getForms(String, Boolean, java.util.Collection, Boolean, java.util.Collection, java.util.Collection, java.util.Collection + + */ + @Test + public void getFormCriteria_shouldNotReturnFormWithMissingFormFieldId() throws DAOException { + executeDataSet(INITIAL_FIELDS_XML); + executeDataSet("org/openmrs/api/include/FormServiceTest-formFields.xml"); + + FormService formService = Context.getFormService(); + + List containingAllFormFields = new ArrayList<>(); + FormField formField1 = new FormField(); + formField1.setFormFieldId(2); + containingAllFormFields.add(formField1); + + FormField formField2 = new FormField(); + formField2.setFormFieldId(7); + containingAllFormFields.add(formField2); + + // the containingAllFormFields list includes a FormField that is not in any of the forms. + FormField formField3 = new FormField(); + formField3.setFormFieldId(8); + containingAllFormFields.add(formField3); + + List forms = formService.getForms(null, null, null, null, null, containingAllFormFields, null); + + assertEquals(0, forms.size()); + } + + /** + * Make sure form is returned with the name starting with the partial name. + * + * @see FormService#getForms(String, Boolean, java.util.Collection, Boolean, java.util.Collection, java.util.Collection, java.util.Collection) + */ + @Test + public void getFormCriteria_shouldReturnFormsWithNameStartingWithPartialName() throws DAOException { + executeDataSet(INITIAL_FIELDS_XML); + executeDataSet("org/openmrs/api/include/FormServiceTest-formFields.xml"); + + FormService formService = Context.getFormService(); + + List forms = formService.getForms("Basic", null, null, null, null, null, null); + + assertTrue(forms.stream().anyMatch(form -> form.getName().startsWith("Basic"))); + } + + /** + * Make sure form is returned with the name containing the partial name + * after a space character. + * + * @see FormService#getForms(String, Boolean, java.util.Collection, Boolean, java.util.Collection, java.util.Collection, java.util.Collection) + */ + @Test + public void getFormCriteria_shouldReturnFormsWithNameContainingPartialName() throws DAOException { + executeDataSet(INITIAL_FIELDS_XML); + executeDataSet("org/openmrs/api/include/FormServiceTest-formFields.xml"); + + FormService formService = Context.getFormService(); + + List forms = formService.getForms("Form", null, null, null, null, null, null); + + assertTrue(forms.stream().anyMatch(form -> form.getName().contains(" Form"))); + } + /** * @ * @see FormService#getForms(String,Boolean,Collection,Boolean,Collection,Collection,Collection) diff --git a/api/src/test/java/org/openmrs/api/OrderServiceTest.java b/api/src/test/java/org/openmrs/api/OrderServiceTest.java index 3cf9ca50c04a..075a7cdb0193 100644 --- a/api/src/test/java/org/openmrs/api/OrderServiceTest.java +++ b/api/src/test/java/org/openmrs/api/OrderServiceTest.java @@ -35,6 +35,8 @@ import org.openmrs.Encounter; import org.openmrs.FreeTextDosingInstructions; import org.openmrs.GlobalProperty; +import org.openmrs.Location; +import org.openmrs.LocationAttributeType; import org.openmrs.MedicationDispense; import org.openmrs.Obs; import org.openmrs.Order; @@ -48,15 +50,21 @@ import org.openmrs.OrderSet; import org.openmrs.OrderType; import org.openmrs.Patient; +import org.openmrs.PatientState; +import org.openmrs.PersonAddress; +import org.openmrs.PersonAttributeType; import org.openmrs.Provider; import org.openmrs.ProviderAttributeType; +import org.openmrs.Relationship; import org.openmrs.SimpleDosingInstructions; import org.openmrs.TestOrder; +import org.openmrs.User; import org.openmrs.Visit; import org.openmrs.VisitAttributeType; import org.openmrs.api.builder.DrugOrderBuilder; import org.openmrs.api.builder.OrderBuilder; import org.openmrs.api.context.Context; +import org.openmrs.api.db.SerializedObject; import org.openmrs.api.db.hibernate.HibernateAdministrationDAO; import org.openmrs.api.db.hibernate.HibernateSessionFactoryBean; import org.openmrs.api.impl.OrderServiceImpl; @@ -306,7 +314,7 @@ public void getOrderHistoryByConcept_shouldReturnOrdersWithTheGivenConcept() { assertEquals(22, orders.get(2).getOrderId().intValue()); assertEquals(2, orders.get(3).getOrderId().intValue()); } - + /** * @see OrderService#getOrderHistoryByConcept(Patient, Concept) */ @@ -2646,7 +2654,17 @@ public void saveOrder_shouldFailIfTheJavaTypeOfThePreviousOrderDoesNotMatch() th .addAnnotatedClass(Diagnosis.class).addAnnotatedClass(Condition.class) .addAnnotatedClass(Visit.class).addAnnotatedClass(VisitAttributeType.class) .addAnnotatedClass(MedicationDispense.class) - .addAnnotatedClass(ProviderAttributeType.class).addAnnotatedClass(ConceptMapType.class).getMetadataBuilder().build(); + .addAnnotatedClass(ProviderAttributeType.class) + .addAnnotatedClass(ConceptMapType.class) + .addAnnotatedClass(Relationship.class) + .addAnnotatedClass(Location.class) + .addAnnotatedClass(PersonAddress.class) + .addAnnotatedClass(PersonAttributeType.class) + .addAnnotatedClass(User.class) + .addAnnotatedClass(LocationAttributeType.class) + .addAnnotatedClass(SerializedObject.class) + .addAnnotatedClass(PatientState.class) + .getMetadataBuilder().build(); Field field = adminDAO.getClass().getDeclaredField("metadata"); @@ -3802,7 +3820,43 @@ public void saveOrderGroup_shouldSavePreviouslySavedOrderGroup() { orderGroup = Context.getOrderService().getOrderGroup(orderGroupId); Context.getOrderService().saveOrderGroup(orderGroup); } - + + @Test + public void getOrderGroupsByPatient_shouldReturnOrderGroupsForTheGivenPatient() { + Patient patient = Context.getPatientService().getPatient(7); + + List orderGroups = orderService.getOrderGroupsByPatient(patient); + + assertNotNull(orderGroups); + assertEquals(2, orderGroups.size()); + + assertTrue(orderGroups.stream().anyMatch(group -> group.getOrderGroupId() == 1)); + assertTrue(orderGroups.stream().anyMatch(group -> group.getOrderGroupId() == 3)); + } + + @Test + public void getOrderGroupsByPatient_shouldThrowAPIExceptionForNullPatient() { + assertThrows(APIException.class, () -> orderService.getOrderGroupsByPatient(null)); + } + + @Test + public void getOrderGroupsByEncounter_shouldReturnOrderGroupsForTheGivenEncounter() { + Encounter encounter = Context.getEncounterService().getEncounter(3); + + List orderGroups = orderService.getOrderGroupsByEncounter(encounter); + + assertNotNull(orderGroups); + assertEquals(2, orderGroups.size()); + + assertTrue(orderGroups.stream().anyMatch(group -> group.getOrderGroupId() == 1)); + assertTrue(orderGroups.stream().anyMatch(group -> group.getOrderGroupId() == 3)); + } + + @Test + public void getOrderGroupsByEncounter_shouldThrowAPIExceptionForNullEncounter() { + assertThrows(APIException.class, () -> orderService.getOrderGroupsByEncounter(null)); + } + @Test public void getOrderGroupAttributeTypes_shouldReturnAllOrderGroupAttributeTypes() { List orderGroupAttributeTypes = orderService.getAllOrderGroupAttributeTypes(); @@ -3953,6 +4007,43 @@ public void saveOrder_shouldAllowARetrospectiveOrderToCloseAnOrderThatExpiredInT encounterService.saveEncounter(e2); assertThat(new SimpleDateFormat("yyyy-MM-dd").format(o1.getDateStopped()), is("2008-08-14")); } + + /** + * @see OrderService#saveOrderGroup(org.openmrs.OrderGroup, OrderContext) + */ + @Test + public void saveOrderGroup_shouldSaveOrderGroupWithOrderContext() { + executeDataSet(ORDER_SET); + Encounter encounter = encounterService.getEncounter(3); + OrderSet orderSet = Context.getOrderSetService().getOrderSet(1); + OrderGroup orderGroup = new OrderGroup(); + orderGroup.setOrderSet(orderSet); + orderGroup.setPatient(encounter.getPatient()); + orderGroup.setEncounter(encounter); + + Order firstOrder = new OrderBuilder().withAction(Order.Action.NEW).withPatient(1).withConcept(10).withOrderer(1) + .withEncounter(3).withDateActivated(new Date()).withOrderType(17) + .withUrgency(Order.Urgency.ON_SCHEDULED_DATE).withScheduledDate(new Date()).build(); + + Order secondOrder = new OrderBuilder().withAction(Order.Action.NEW).withPatient(7).withConcept(10).withOrderer(1) + .withEncounter(3).withDateActivated(new Date()).withOrderType(17) + .withUrgency(Order.Urgency.ON_SCHEDULED_DATE).withScheduledDate(new Date()).build(); + + orderGroup.addOrder(firstOrder); + orderGroup.addOrder(secondOrder); + + OrderType orderType = orderService.getOrderType(17); + CareSetting careSetting = orderService.getCareSetting(1); + + OrderContext orderContext = new OrderContext(); + orderContext.setCareSetting(careSetting); + orderContext.setOrderType(orderType); + OrderGroup result = orderService.saveOrderGroup(orderGroup, orderContext); + + assertEquals(2, result.getOrders().size()); + assertEquals(orderType, result.getOrders().get(0).getOrderType()); + assertEquals(careSetting, result.getOrders().get(1).getCareSetting()); + } /** * @see OrderService#saveOrder(org.openmrs.Order, OrderContext) @@ -4054,4 +4145,19 @@ public void getOrderAttributeByUuid_shouldReturnOrderAttributeUsingProvidedUuid( assertEquals("Testing Reference", orderAttribute.getValueReference()); assertEquals(1, orderAttribute.getId()); } + + @Test + public void getOrderAttributeTypeByName_shouldReturnCorrectOrderAttributeType() { + final String ORDER_ATTRIBUTE_TYPE_NAME = "Supplies"; + + OrderAttributeType orderAttributeType = orderService.getOrderAttributeTypeByName(ORDER_ATTRIBUTE_TYPE_NAME); + + assertNotNull(orderAttributeType); + assertEquals(ORDER_ATTRIBUTE_TYPE_NAME, orderAttributeType.getName()); + } + + @Test + public void getOrderAttributeTypeByName_shouldReturnNullForMismatchedName() { + assertNull(orderService.getOrderAttributeTypeByName("InvalidName")); + } } diff --git a/api/src/test/java/org/openmrs/api/OrderSetServiceTest.java b/api/src/test/java/org/openmrs/api/OrderSetServiceTest.java index 235dcb5d97e1..5a9dc409b914 100644 --- a/api/src/test/java/org/openmrs/api/OrderSetServiceTest.java +++ b/api/src/test/java/org/openmrs/api/OrderSetServiceTest.java @@ -42,6 +42,9 @@ public class OrderSetServiceTest extends BaseContextSensitiveTest { protected ConceptService conceptService; + protected static final String AUDIT_DATE = "Audit Date"; + protected static final String INVALID_AUDIT_DATE = "Non existent name"; + protected static final String ORDER_SET = "org/openmrs/api/include/OrderSetServiceTest-general.xml"; protected static final String ORDER_SET_ATTRIBUTES = "org/openmrs/api/include/OrderSetServiceTest-attributes.xml"; @@ -383,7 +386,7 @@ public void getOrderSetAttributeByUuid_shouldReturnNullIfNoOrderSetAttributeHasT @Test public void getOrderSetAttributeTypeByUuid_shouldReturnTheOrderSetAttributeTypeWithTheGivenUuid() { executeDataSet(ORDER_SET_ATTRIBUTES); - assertEquals("Audit Date", Context.getOrderSetService().getOrderSetAttributeTypeByUuid( + assertEquals(AUDIT_DATE, Context.getOrderSetService().getOrderSetAttributeTypeByUuid( "8516cc50-6f9f-33e0-8414-001e648eb67e").getName()); } @@ -475,5 +478,28 @@ public void unretireOrderSetAttributeType_shouldUnretireARetiredOrderSetAttribut assertNull(orderSetAttributeType.getRetireReason()); } + /** + * @see OrderSetService#getOrderSetAttributeTypeByName(String) + */ + @Test + public void getOrderSetAttributeTypeByName_shouldGetMatchingOrderSetAttributeType() { + executeDataSet(ORDER_SET_ATTRIBUTES); + + OrderSetAttributeType attributeType = orderSetService.getOrderSetAttributeTypeByName(AUDIT_DATE); + + assertNotNull(attributeType, "The fetched OrderSetAttributeType should not be null"); + assertEquals(AUDIT_DATE, attributeType.getName(), "The name of the fetched attribute type should match the requested name"); + } + /** + * @see OrderSetService#getOrderSetAttributeTypeByName(String) + */ + @Test + public void getOrderSetAttributeTypeByName_shouldReturnNullForNonExistentName() { + executeDataSet(ORDER_SET_ATTRIBUTES); + + OrderSetAttributeType attributeType = orderSetService.getOrderSetAttributeTypeByName(INVALID_AUDIT_DATE); + + assertNull(attributeType, "The fetched OrderSetAttributeType should be null"); + } } diff --git a/api/src/test/java/org/openmrs/api/PatientServiceTest.java b/api/src/test/java/org/openmrs/api/PatientServiceTest.java index 3cb514d7d101..f0b352892568 100644 --- a/api/src/test/java/org/openmrs/api/PatientServiceTest.java +++ b/api/src/test/java/org/openmrs/api/PatientServiceTest.java @@ -3294,4 +3294,10 @@ public void getPatientIdentifiersByPatientProgram_shouldGetPatientsByIdentifierB assertEquals(patientIdentifier.iterator().next().getIdentifier(), "XXXCCCAAA11"); } + @Test + public void getIdentifierValidator_shouldThrowPatientIdentifierExceptionWhenClassNotFound() throws Exception { + PatientIdentifierException patientIdentifierException = assertThrows(PatientIdentifierException.class, () -> patientService.getIdentifierValidator("com.example.InvalidIdentifierValidator")); + assertEquals("Could not find patient identifier validator com.example.InvalidIdentifierValidator", patientIdentifierException.getMessage()); + } + } diff --git a/api/src/test/java/org/openmrs/api/PersonServiceTest.java b/api/src/test/java/org/openmrs/api/PersonServiceTest.java index 753717afcc63..7c5caa422fce 100644 --- a/api/src/test/java/org/openmrs/api/PersonServiceTest.java +++ b/api/src/test/java/org/openmrs/api/PersonServiceTest.java @@ -13,6 +13,7 @@ import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -55,6 +56,7 @@ import org.openmrs.test.jupiter.BaseContextSensitiveTest; import org.openmrs.test.TestUtil; import org.openmrs.util.OpenmrsConstants; +import org.openmrs.util.OpenmrsObjectIdMatcher; /** * This class tests methods in the PersonService class. TODO: Test all methods in the PersonService @@ -549,7 +551,6 @@ public void getSimilarPeople_shouldMatchN1InThreeNamesSearch() throws Exception assertTrue(containsId(matches, 1003)); } - /** * @see PersonService#getSimilarPeople(String,Integer,String) */ @@ -557,42 +558,14 @@ public void getSimilarPeople_shouldMatchN1InThreeNamesSearch() throws Exception public void getSimilarPeople_shouldMatchN2InTwoNamesSearch() throws Exception { executeDataSet("org/openmrs/api/include/PersonServiceTest-names.xml"); updateSearchIndex(); - - Set matches = Context.getPersonService().getSimilarPeople("D Graham", 1979, "M"); - assertEquals(8, matches.size()); - assertTrue(containsId(matches, 1010)); - assertTrue(containsId(matches, 1011)); - assertTrue(containsId(matches, 1013)); - - assertTrue(containsId(matches, 1006)); - assertTrue(containsId(matches, 1003)); - assertTrue(containsId(matches, 1007)); - assertTrue(containsId(matches, 1004)); - assertTrue(containsId(matches, 1005)); - } - - - /** - * @see PersonService#getSimilarPeople(String,Integer,String) - */ - @Test - public void getSimilarPeople_shouldMatchN2InOneLastNameAndEmptyNames() throws Exception { - executeDataSet("org/openmrs/api/include/PersonServiceTest-names.xml"); - updateSearchIndex(); - - Set matches = Context.getPersonService().getSimilarPeople("D Graham", 1979, "M"); - assertEquals(8, matches.size()); - assertTrue(containsId(matches, 1010)); - assertTrue(containsId(matches, 1011)); - assertTrue(containsId(matches, 1013)); - - assertTrue(containsId(matches, 1006)); - assertTrue(containsId(matches, 1003)); - assertTrue(containsId(matches, 1007)); - assertTrue(containsId(matches, 1004)); - assertTrue(containsId(matches, 1005)); - + Set matches = Context.getPersonService().getSimilarPeople("D Graham", 1979, "M"); + // Changed containsInAnyOrder to hasItems as the returned list is unstable for some reason and may include + // additional item. + assertThat(matches, hasItems(new OpenmrsObjectIdMatcher(1010), + new OpenmrsObjectIdMatcher(1011), new OpenmrsObjectIdMatcher(1013), new OpenmrsObjectIdMatcher(1006), + new OpenmrsObjectIdMatcher(1003), new OpenmrsObjectIdMatcher(1007), new OpenmrsObjectIdMatcher(1004), + new OpenmrsObjectIdMatcher(1005))); } /** diff --git a/api/src/test/java/org/openmrs/api/ProgramWorkflowServiceTest.java b/api/src/test/java/org/openmrs/api/ProgramWorkflowServiceTest.java index 3a488f5e924b..8b4fef4d74ce 100644 --- a/api/src/test/java/org/openmrs/api/ProgramWorkflowServiceTest.java +++ b/api/src/test/java/org/openmrs/api/ProgramWorkflowServiceTest.java @@ -774,6 +774,19 @@ public void getPrograms_shouldTestGetPrograms() { assertEquals(malPrograms.size(), 1); assertEquals(prPrograms.size(), 3); } + + @Test + public void findPrograms_shouldReturnProgramsOrderedByName() { + List programs = pws.getPrograms("PR"); // Replace "Test" with a relevant name fragment + + assertTrue(programs.size() > 1, "Should return more than one program for a valid test"); + for (int i = 0; i < programs.size() - 1; i++) { + Program current = programs.get(i); + Program next = programs.get(i + 1); + assertTrue(current.getName().compareToIgnoreCase(next.getName()) <= 0, + "Programs should be ordered by name"); + } + } @Test public void retireProgram_shouldSetRetiredStateToFalseAndSetAReason() { diff --git a/api/src/test/java/org/openmrs/api/ProviderServiceTest.java b/api/src/test/java/org/openmrs/api/ProviderServiceTest.java index 580a5e358337..295606dd4be7 100644 --- a/api/src/test/java/org/openmrs/api/ProviderServiceTest.java +++ b/api/src/test/java/org/openmrs/api/ProviderServiceTest.java @@ -154,6 +154,16 @@ public void getProviderAttributeTypeByUuid_shouldGetTheProviderAttributeTypeByIt assertEquals("Audit Date", providerAttributeType.getName()); } + /** + * @see ProviderService#getProviderAttributeTypeByName(String) + */ + @Test + public void getProviderAttributeTypeByName_shouldGetTheProviderAttributeTypeByItsName() { + ProviderAttributeType providerAttributeType = service.getProviderAttributeTypeByName("Audit Date"); + assertEquals("Audit Date", providerAttributeType.getName()); + assertEquals("9516cc50-6f9f-11e0-8414-001e378eb67e", providerAttributeType.getUuid()); + } + /** * @see ProviderService#getProviderByUuid(String) */ @@ -288,6 +298,32 @@ public void saveProviderAttributeType_shouldSaveTheProviderAttributeType() { assertNotNull(providerAttributeType.getId()); } + /** + * @see ProviderService#saveProviderAttributeType(ProviderAttributeType) + */ + @Test + public void saveProviderAttributeType_shouldNotSaveProviderAttributeTypeWithDuplicateName() { + //duplication + ProviderAttributeType duplicatedAttributeType = new ProviderAttributeType(); + duplicatedAttributeType.setName("Audit Date"); + duplicatedAttributeType.setDatatypeClassname(FreeTextDatatype.class.getName()); + + assertThrows(ValidationException.class, () -> { + service.saveProviderAttributeType(duplicatedAttributeType); + }); + } + + @Test + public void saveProviderAttributeType_shouldSaveProviderAttributeTypeWithSameNameAsRetiredType() { + int size = service.getAllProviderAttributeTypes().size(); + ProviderAttributeType providerAttributeType = new ProviderAttributeType(); + providerAttributeType.setName("A Date We Don't Care About"); + providerAttributeType.setDatatypeClassname(FreeTextDatatype.class.getName()); + providerAttributeType = service.saveProviderAttributeType(providerAttributeType); + assertEquals(size + 1, service.getAllProviderAttributeTypes().size()); + assertNotNull(providerAttributeType.getId()); + } + /** * @see ProviderService#unretireProvider(Provider) */ diff --git a/api/src/test/java/org/openmrs/api/UserServiceTest.java b/api/src/test/java/org/openmrs/api/UserServiceTest.java index 950a3085ab2f..1b3b1bc61263 100644 --- a/api/src/test/java/org/openmrs/api/UserServiceTest.java +++ b/api/src/test/java/org/openmrs/api/UserServiceTest.java @@ -9,8 +9,10 @@ */ package org.openmrs.api; -import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -28,6 +30,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Properties; import java.util.Set; import org.apache.commons.lang3.reflect.FieldUtils; @@ -163,11 +166,11 @@ public void createUser_shouldShouldCreateUserWhoIsPatientAlready() throws SQLExc Context.clearSession(); List allUsers = userService.getAllUsers(); - assertEquals(12, allUsers.size()); + assertThat(allUsers, hasSize(greaterThanOrEqualTo(12))); // there should still only be the one patient we created in the xml file List allPatientsSet = Context.getPatientService().getAllPatients(); - assertEquals(4, allPatientsSet.size()); + assertThat(allUsers, hasSize(greaterThanOrEqualTo(4))); } @Test @@ -390,6 +393,33 @@ public void changePassword_shouldBeAbleToUpdatePasswordMultipleTimes() { userService.changePassword("test", "Tester12"); userService.changePassword("Tester12", "Tester13"); } + + @Test + public void changePassword_shouldRespectLockingViaRuntimeProperty() { + assertThat("admin", is(Context.getAuthenticatedUser().getUsername())); + User u = userService.getUserByUsername(ADMIN_USERNAME); + + assertThat(u.isSuperUser(), is(true)); + + Properties props = Context.getRuntimeProperties(); + props.setProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY, "true"); + Context.setRuntimeProperties(props); + + APIException apiException = assertThrows(APIException.class, () -> userService.changePassword(u,"test", "SuperAdmin123")); + + assertThat(apiException.getMessage(), is("admin.password.is.locked")); + + props.setProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY, "True"); + Context.setRuntimeProperties(props); + + apiException = assertThrows(APIException.class, () -> userService.changePassword(u,"test", "SuperAdmin123")); + assertThat(apiException.getMessage(), is("admin.password.is.locked")); + + props.remove(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY); + Context.setRuntimeProperties(props); + + userService.changePassword(u,"test", "SuperAdmin123"); + } @Test public void saveUser_shouldGrantNewRolesInRolesListToUser() { @@ -658,7 +688,7 @@ public void getAllRoles_shouldReturnAllRolesInTheSystem() { @Test public void getAllUsers_shouldFetchAllUsersInTheSystem() { List users = userService.getAllUsers(); - assertEquals(3, users.size()); + assertThat(users, hasSize(greaterThanOrEqualTo(3))); } /** @@ -808,8 +838,8 @@ public void getUsers_shouldFetchVoidedUsersIfIncludedVoidedIsTrue() { */ @Test public void getUsers_shouldFetchAllUsersIfNameSearchIsEmptyOrNull() { - assertEquals(3, userService.getUsers("", null, true).size()); - assertEquals(3, userService.getUsers(null, null, true).size()); + assertThat(userService.getUsers("", null, true), hasSize(greaterThanOrEqualTo(3))); + assertThat(userService.getUsers(null, null, true), hasSize(greaterThanOrEqualTo(3))); } /** @@ -1249,7 +1279,7 @@ public void getUsers_shouldNotFailIfRolesAreSearchedButNameIsEmpty() { List roles = new ArrayList<>(); roles.add(role); - assertEquals(2, userService.getUsers("", roles, true).size()); + assertThat(userService.getUsers("", roles, true), hasSize(greaterThanOrEqualTo(2))); } /** @@ -1275,7 +1305,7 @@ public void saveUserProperty_shouldAddNewPropertyToExistingUserProperties() { Context.authenticate(user.getUsername(), "testUser1234"); final int numberOfUserProperties = user.getUserProperties().size(); - assertEquals(1, user.getUserProperties().size()); + assertEquals(2, user.getUserProperties().size()); final String USER_PROPERTY_KEY = "test-key"; final String USER_PROPERTY_VALUE = "test-value"; @@ -1294,7 +1324,7 @@ public void saveUserProperties_shouldRemoveAllExistingPropertiesAndAssignNewProp // retrieve a user who has UserProperties User user = userService.getUser(5511); - assertEquals(1, user.getUserProperties().size()); + assertEquals(2, user.getUserProperties().size()); // Authenticate the test user so that Context.getAuthenticatedUser() method returns above user Context.authenticate(user.getUsername(), "testUser1234"); final String USER_PROPERTY_KEY_1 = "test-key1"; @@ -1438,7 +1468,7 @@ public void changePassword_shouldUpdatePasswordOfGivenUserWhenLoggedInUserHasEdi User user = userService.getUserByUsername(ADMIN_USERNAME); assertNotNull(user, "There needs to be a user with username 'admin' in the database"); - userService.changePassword(user, "testTest123"); + userService.changePassword(user, "test", "testTest123"); Context.authenticate(user.getUsername(), "testTest123"); } @@ -1449,7 +1479,9 @@ public void changePassword_shouldNotUpdatePasswordOfGivenUserWhenLoggedInUserDoe User user = userService.getUser(6001); assertFalse(user.hasPrivilege(PrivilegeConstants.EDIT_USER_PASSWORDS)); Context.authenticate(user.getUsername(), "userServiceTest"); - APIAuthenticationException exception = assertThrows(APIAuthenticationException.class, () -> userService.changePassword(user, "testTest123")); + + APIAuthenticationException exception = assertThrows(APIAuthenticationException.class, () -> userService.changePassword(user, "userServiceTest", "testTest123")); + assertThat(exception.getMessage(), is(messages.getMessage("error.privilegesRequired", new Object[] {PrivilegeConstants.EDIT_USER_PASSWORDS}, null))); } @@ -1631,6 +1663,15 @@ public void getDefaultLocaleForUser_shouldReturnDefaultLocaleForUserIfConfigured assertEquals(Locale.FRENCH, Context.getUserService().getDefaultLocaleForUser(createdUser)); } + @Test + public void getDefaultLocaleForUser_shouldReturnDefaultLocaleForUserIfAlreadySet() { + executeDataSet(XML_FILENAME); + Context.authenticate("test", "testUser1234"); + + Locale locale = Context.getLocale(); + assertEquals(Locale.FRENCH, locale); + } + private User createTestUser() { User u = new User(); u.setPerson(new Person()); diff --git a/api/src/test/java/org/openmrs/api/context/ContextTest.java b/api/src/test/java/org/openmrs/api/context/ContextTest.java index 425a30dea044..a68d0d28f02d 100644 --- a/api/src/test/java/org/openmrs/api/context/ContextTest.java +++ b/api/src/test/java/org/openmrs/api/context/ContextTest.java @@ -191,7 +191,8 @@ public void refreshAuthenticatedUser_shouldSetDefaultLocationIfLocationNull() { Context.flushSession(); Context.evictFromSession(evictedUser); - Context.refreshAuthenticatedUser(); + Context.logout(); + authenticate(); assertEquals(Context.getLocationService().getLocation(2), Context.getUserContext().getLocation()); } diff --git a/api/src/test/java/org/openmrs/api/context/ServiceContextTest.java b/api/src/test/java/org/openmrs/api/context/ServiceContextTest.java new file mode 100644 index 000000000000..095df8c2759f --- /dev/null +++ b/api/src/test/java/org/openmrs/api/context/ServiceContextTest.java @@ -0,0 +1,99 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.api.context; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.openMocks; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openmrs.api.APIException; +import org.openmrs.test.jupiter.BaseContextSensitiveTest; +import org.openmrs.util.DatabaseUpdateException; +import org.openmrs.util.InputRequiredException; + +public class ServiceContextTest extends BaseContextSensitiveTest { + + private ServiceContext serviceContext; + + private ServiceContext spiedServiceContext; + + private boolean isUseSystemClassLoader; + + @BeforeEach + public void setUp() throws InputRequiredException, DatabaseUpdateException { + openMocks(this); + serviceContext = Context.getServiceContext(); + spiedServiceContext = spy(serviceContext); + isUseSystemClassLoader = serviceContext.isUseSystemClassLoader(); + } + + @AfterEach + public void tearDown() { + serviceContext.setUseSystemClassLoader(isUseSystemClassLoader); + } + + @Test + public void getModuleOpenmrsServices_shouldRaiseApiExceptionWithNonExistentClass() { + List params = new ArrayList<>(); + params.add("org.openmrs.module.webservices.rest.nonexistent.InvalidClass"); + params.add(Object.class); + + spiedServiceContext.setUseSystemClassLoader(false); + APIException thrownException = assertThrows(APIException.class, () -> spiedServiceContext.setModuleService(params)); + + assertNotNull(thrownException.getMessage()); + assertNotNull(thrownException.getCause()); + + verify(spiedServiceContext, never()).getMessageService(); + verify(spiedServiceContext, never()).getMessageSourceService(); + } + + @Test + public void getModuleOpenmrsServices_shouldRaiseApiExceptionWithNullClass() { + List params = new ArrayList<>(); + params.add(null); + params.add(Object.class); + + spiedServiceContext.setUseSystemClassLoader(false); + APIException thrownException = assertThrows(APIException.class, () -> spiedServiceContext.setModuleService(params)); + + assertNotNull(thrownException.getMessage()); + assertNull(thrownException.getCause()); + + verify(spiedServiceContext, never()).getMessageService(); + verify(spiedServiceContext, never()).getMessageSourceService(); + } + + @Test + public void getModuleOpenmrsServices_shouldRaiseApiExceptionWithNullClassInstance() { + List params = new ArrayList<>(); + params.add("org.openmrs.module.webservices.rest.nonexistent.InvalidClass"); + params.add(null); + + spiedServiceContext.setUseSystemClassLoader(false); + APIException thrownException = assertThrows(APIException.class, () -> spiedServiceContext.setModuleService(params)); + + assertNotNull(thrownException.getMessage()); + assertNull(thrownException.getCause()); + + verify(spiedServiceContext, never()).getMessageService(); + verify(spiedServiceContext, never()).getMessageSourceService(); + } +} diff --git a/api/src/test/java/org/openmrs/api/context/UserContextTest.java b/api/src/test/java/org/openmrs/api/context/UserContextTest.java new file mode 100644 index 000000000000..d4ec00ea4d0a --- /dev/null +++ b/api/src/test/java/org/openmrs/api/context/UserContextTest.java @@ -0,0 +1,114 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.api.context; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openmrs.Person; +import org.openmrs.PersonName; +import org.openmrs.User; +import org.openmrs.api.PersonService; +import org.openmrs.api.UserService; +import org.openmrs.test.jupiter.BaseContextSensitiveTest; +import org.openmrs.util.OpenmrsConstants; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +public class UserContextTest extends BaseContextSensitiveTest { + + @Autowired + private UserService userService; + + @Autowired + private PersonService personService; + + Person testPerson; + + User testUser; + + @BeforeEach + void createUser() { + testPerson = new Person(); + testPerson.addName(new PersonName("Carroll", "", "Deacon")); + testPerson.setGender("U"); + testPerson = personService.savePerson(testPerson); + + testUser = new User(); + testUser.setUsername("testUser"); + testUser.setPerson(testPerson); + testUser = userService.createUser(testUser, "Test1234"); + } + + @AfterEach + void deleteUser() { + userService.purgeUser(testUser); + personService.purgePerson(testPerson); + } + + @Test + void getDefaultLocationId_shouldGetDefaultLocationById() { + // arrange + Context.getUserContext().setLocationId(null); + testUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCATION, "1"); + userService.saveUser(testUser); + + // act + Integer locationId = Context.getUserContext().getDefaultLocationId(testUser); + + // assert + assertThat(locationId, equalTo(1)); + } + + @Test + void getDefaultLocationId_shouldGetDefaultLocationByUuid() { + // arrange + Context.getUserContext().setLocationId(null); + testUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCATION, "8d6c993e-c2cc-11de-8d13-0010c6dffd0f"); + userService.saveUser(testUser); + + // act + Integer locationId = Context.getUserContext().getDefaultLocationId(testUser); + + // assert + assertThat(locationId, equalTo(1)); + } + + @Test + void getDefaultLocationId_shouldReturnNullForInvalidId() { + // arrange + Context.getUserContext().setLocationId(null); + testUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCATION, String.valueOf(Integer.MAX_VALUE)); + userService.saveUser(testUser); + + // act + Integer locationId = Context.getUserContext().getDefaultLocationId(testUser); + + // assert + assertThat(locationId, nullValue()); + } + + @Test + void getDefaultLocationId_shouldReturnNullForInvalidUuid() { + // arrange + Context.getUserContext().setLocationId(null); + testUser.setUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCATION, "0e32f474-eca5-4cc2-a64d-53b086f27e52"); + userService.saveUser(testUser); + + // act + Integer locationId = Context.getUserContext().getDefaultLocationId(testUser); + + // assert + assertThat(locationId, nullValue()); + } +} diff --git a/api/src/test/java/org/openmrs/api/db/ConceptDAOTest.java b/api/src/test/java/org/openmrs/api/db/ConceptDAOTest.java index 2de161515248..a36fb3c8927c 100644 --- a/api/src/test/java/org/openmrs/api/db/ConceptDAOTest.java +++ b/api/src/test/java/org/openmrs/api/db/ConceptDAOTest.java @@ -226,5 +226,28 @@ public void getConcepts_shouldReturnCorrectResultsIfAConceptNameContainsSameWord assertEquals(c1, searchResults1.get(0).getConcept()); assertEquals(cn1b, searchResults1.get(0).getConceptName()); } - + + /** + * @see {@link ConceptDAO#getConcepts(String, List, boolean, List, List, List, List, Concept, Integer, Integer)} + */ + @SuppressWarnings("unchecked") + @Test + public void getConcepts_shouldBeDiacriticInsensitive() { + executeDataSet("org/openmrs/api/include/ConceptServiceTest-accents.xml"); + Context.getConceptService().updateConceptIndexes(); + List searchResults = dao.getConcepts( + "Hysterectom", + Collections.singletonList(Locale.ENGLISH), + false, + Collections.EMPTY_LIST, + Collections.EMPTY_LIST, + Collections.EMPTY_LIST, + Collections.EMPTY_LIST, + null, + null, + null + ); + assertEquals(4, searchResults.size()); + assertEquals("HystÊrectomie", searchResults.get(0).getConcept().getName().getName()); + } } diff --git a/api/src/test/java/org/openmrs/api/db/hibernate/HibernateConceptDAOTest.java b/api/src/test/java/org/openmrs/api/db/hibernate/HibernateConceptDAOTest.java index 58843f4e28b3..d93dde4b4fcd 100644 --- a/api/src/test/java/org/openmrs/api/db/hibernate/HibernateConceptDAOTest.java +++ b/api/src/test/java/org/openmrs/api/db/hibernate/HibernateConceptDAOTest.java @@ -12,6 +12,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import java.util.Locale; @@ -22,7 +24,11 @@ import org.openmrs.ConceptAttributeType; import org.openmrs.ConceptClass; import org.openmrs.ConceptDatatype; +import org.openmrs.ConceptMap; +import org.openmrs.ConceptMapType; import org.openmrs.ConceptName; +import org.openmrs.ConceptReferenceTerm; +import org.openmrs.ConceptSource; import org.openmrs.Drug; import org.openmrs.api.ConceptNameType; import org.openmrs.api.context.Context; @@ -151,7 +157,6 @@ public void getDrugs_shouldReturnNonRetired() { public void getDrugs_shouldReturnDrugEvenIf_DrugNameHasSpecialCharacters() { List drugList1 = dao.getDrugs("DRUG_NAME_WITH_SPECIAL_CHARACTERS (", null, true); assertEquals(1, drugList1.size()); - } /** @@ -189,4 +194,51 @@ public void isConceptNameDuplicate_shouldNotFailIfConceptDoesNotHaveADefaultName assertThat(duplicate, is(false)); } + @Test + public void getConceptIdsByMapping_shouldReturnDistinctConceptIds() { + ConceptSource source = dao.getConceptSourceByName("Some Standardized Terminology"); + ConceptMapType sameAs = dao.getConceptMapTypeByName("same-as"); + Concept weightConcept = dao.getConcept(5089); + assertNotNull(source); + List conceptIds = dao.getConceptIdsByMapping("WGT234", source.getName(), true); + assertEquals(1, conceptIds.size()); + assertEquals(weightConcept.getConceptId(), conceptIds.get(0)); + + // Add another mapping that matches + ConceptReferenceTerm term = new ConceptReferenceTerm(source, "wgt234", null); + weightConcept.addConceptMapping(new ConceptMap(term, sameAs)); + dao.saveConcept(weightConcept); + + // Querying by this mapping should only return the weight concept id once, even if 2 of its terms match + conceptIds = dao.getConceptIdsByMapping("WGT234", source.getName(), true); + assertEquals(1, conceptIds.size()); + assertEquals(weightConcept.getConceptId(), conceptIds.get(0)); + } + + /** + * @see HibernateConceptDAO#getConceptDatatypes(String) + */ + @Test + public void getConceptDatatypes_shouldReturnDatatypesWithNameStartingWithGivenString() { + String namePrefix = "Numeric"; + + List datatypes = dao.getConceptDatatypes(namePrefix); + + assertTrue(datatypes.size() > 0); + for (ConceptDatatype datatype : datatypes) { + assertTrue(datatype.getName().startsWith(namePrefix)); + } + } + + /** + * @see HibernateConceptDAO#getConceptDatatypes(String) + */ + @Test + public void getConceptDatatypes_shouldReturnEmptyListForNonExistentName() { + String nonExistentPrefix = "NonExistent"; + + List datatypes = dao.getConceptDatatypes(nonExistentPrefix); + + assertTrue(datatypes.isEmpty()); + } } diff --git a/api/src/test/java/org/openmrs/api/db/hibernate/HibernateConditionDAOTest.java b/api/src/test/java/org/openmrs/api/db/hibernate/HibernateConditionDAOTest.java index ea96fc77c9c0..e14da2c0c967 100644 --- a/api/src/test/java/org/openmrs/api/db/hibernate/HibernateConditionDAOTest.java +++ b/api/src/test/java/org/openmrs/api/db/hibernate/HibernateConditionDAOTest.java @@ -116,8 +116,10 @@ public void shouldGetCondition() { public void shouldGetAllConditions() { Patient patient = new Patient(2); List conditions = dao.getAllConditions(patient); - assertEquals(3, conditions.size()); + patient = new Patient(8); + conditions = dao.getAllConditions(patient); + assertEquals(7, conditions.size()); } @Test @@ -125,6 +127,9 @@ public void shouldGetActiveConditions() { Patient patient = new Patient(2); List active = dao.getActiveConditions(patient); assertEquals(1, active.size()); + patient = new Patient(8); + active = dao.getActiveConditions(patient); + assertEquals(3, active.size()); } @Test diff --git a/api/src/test/java/org/openmrs/api/db/hibernate/HibernateFormDAOTest.java b/api/src/test/java/org/openmrs/api/db/hibernate/HibernateFormDAOTest.java index f1af8edeb650..8460c59c2a3b 100644 --- a/api/src/test/java/org/openmrs/api/db/hibernate/HibernateFormDAOTest.java +++ b/api/src/test/java/org/openmrs/api/db/hibernate/HibernateFormDAOTest.java @@ -10,6 +10,7 @@ package org.openmrs.api.db.hibernate; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Arrays; import java.util.Collections; @@ -18,6 +19,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openmrs.Field; +import org.openmrs.Form; import org.openmrs.FormField; import org.openmrs.test.jupiter.BaseContextSensitiveTest; import org.springframework.beans.factory.annotation.Autowired; @@ -43,4 +45,18 @@ public void shouldFilterAgainstFormFields() { assertEquals(0, (Object)dao.getForms(null, false, Collections.emptyList(), null, formFields, formFields, Arrays.asList(new Field(3))).size()); } + + @Test + public void shouldGetFormFieldsByForm() { + Form form = new Form(2); + List formFields = dao.getFormFields(form); + + assertNotNull(formFields); + + final int EXPECTED_SIZE = 2; + assertEquals(EXPECTED_SIZE, formFields.size()); + for (FormField formField : formFields) { + assertEquals(form.getFormId(), formField.getForm().getFormId()); + } + } } diff --git a/api/src/test/java/org/openmrs/api/db/hibernate/HibernateObsDAOTest.java b/api/src/test/java/org/openmrs/api/db/hibernate/HibernateObsDAOTest.java index 11fa1a6f0a9a..6a8aa594c2f5 100644 --- a/api/src/test/java/org/openmrs/api/db/hibernate/HibernateObsDAOTest.java +++ b/api/src/test/java/org/openmrs/api/db/hibernate/HibernateObsDAOTest.java @@ -11,16 +11,20 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Root; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.criterion.Order; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openmrs.Obs; +import org.openmrs.Person; import org.openmrs.test.jupiter.BaseContextSensitiveTest; /** @@ -47,40 +51,50 @@ public void setUp() { @Test public void getObservations_shouldBeOrderedCorrectly() { Session session = sessionFactory.getCurrentSession(); - + CriteriaBuilder cb = session.getCriteriaBuilder(); List obsListActual; List obsListExpected; - + //Order by id desc - obsListExpected = session.createCriteria(Obs.class, "obs").addOrder(Order.desc("id")).list(); - - obsListActual = dao.getObservations(null, null, null, null, null, null, Collections.singletonList("id"), null, null, null, null, - false, null); - assertArrayEquals(obsListExpected.toArray(), obsListActual.toArray()); - - obsListActual = dao.getObservations(null, null, null, null, null, null, Collections.singletonList("id desc"), null, null, null, - null, false, null); + CriteriaQuery cqDesc = cb.createQuery(Obs.class); + Root rootDesc = cqDesc.from(Obs.class); + cqDesc.orderBy(cb.desc(rootDesc.get("obsId"))); + obsListExpected = session.createQuery(cqDesc).getResultList(); + + obsListActual = dao.getObservations(null, null, null, null, null, null, Collections.singletonList("obsId desc"), null, null, null, + null, false, null); assertArrayEquals(obsListExpected.toArray(), obsListActual.toArray()); - - //Order by id asc - obsListExpected = session.createCriteria(Obs.class, "obs").addOrder(Order.asc("id")).list(); - obsListActual = dao.getObservations(null, null, null, null, null, null, Collections.singletonList("id asc"), null, null, null, - null, false, null); + + //Order by obsId asc + CriteriaQuery cqAsc = cb.createQuery(Obs.class); + Root rootAsc = cqAsc.from(Obs.class); + cqAsc.orderBy(cb.asc(rootAsc.get("obsId"))); + obsListExpected = session.createQuery(cqAsc).getResultList(); + + obsListActual = dao.getObservations(null, null, null, null, null, null, Collections.singletonList("obsId asc"), null, null, null, + null, false, null); assertArrayEquals(obsListExpected.toArray(), obsListActual.toArray()); - - //Order by person_id asc and id desc - obsListExpected = session.createCriteria(Obs.class, "obs").addOrder(Order.asc("person.id")).addOrder(Order.desc("id")).list(); - - obsListActual = dao.getObservations(null, null, null, null, null, null, Arrays.asList("person.id asc", "id"), null, - null, null, null, false, null); + + // Order by person_id asc and id desc + CriteriaQuery cqAscDesc = cb.createQuery(Obs.class); + Root rootAscDesc = cqAscDesc.from(Obs.class); + Join personJoinAscDesc = rootAscDesc.join("person"); + cqAscDesc.orderBy(cb.asc(personJoinAscDesc.get("personId")), cb.desc(rootAscDesc.get("obsId"))); + obsListExpected = session.createQuery(cqAscDesc).getResultList(); + + obsListActual = dao.getObservations(null, null, null, null, null, null, Arrays.asList("personId asc", "obsId desc"), null, + null, null, null, false, null); assertArrayEquals(obsListExpected.toArray(), obsListActual.toArray()); - + //Order by person_id asc and id asc - obsListExpected = session.createCriteria(Obs.class, "obs").addOrder(Order.asc("person.id")) - .addOrder(Order.asc("id")).list(); - - obsListActual = dao.getObservations(null, null, null, null, null, null, Arrays.asList("person.id asc", "id asc"), - null, null, null, null, false, null); + CriteriaQuery cqAscAsc = cb.createQuery(Obs.class); + Root rootAscAsc = cqAscAsc.from(Obs.class); + Join personJoinAscAsc = rootAscAsc.join("person"); + cqAscAsc.orderBy(cb.asc(personJoinAscAsc.get("personId")), cb.asc(rootAscAsc.get("obsId"))); + obsListExpected = session.createQuery(cqAscAsc).getResultList(); + + obsListActual = dao.getObservations(null, null, null, null, null, null, Arrays.asList("personId asc", "obsId asc"), + null, null, null, null, false, null); assertArrayEquals(obsListExpected.toArray(), obsListActual.toArray()); } } diff --git a/api/src/test/java/org/openmrs/api/db/hibernate/HibernatePersonDAOTest.java b/api/src/test/java/org/openmrs/api/db/hibernate/HibernatePersonDAOTest.java index b4c54820456d..f5b97960256f 100644 --- a/api/src/test/java/org/openmrs/api/db/hibernate/HibernatePersonDAOTest.java +++ b/api/src/test/java/org/openmrs/api/db/hibernate/HibernatePersonDAOTest.java @@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -22,6 +23,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openmrs.Person; +import org.openmrs.RelationshipType; +import org.openmrs.api.PersonService; import org.openmrs.api.context.Context; import org.openmrs.test.jupiter.BaseContextSensitiveTest; import org.openmrs.util.GlobalPropertiesTestHelper; @@ -641,4 +644,26 @@ public void savePerson_shouldSavePersonWithBirthDateTime() throws ParseException assertEquals(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2012-05-29 15:23:56"), savedPerson.getBirthDateTime()); } + /** + * @see HibernatePersonDAO#getRelationshipTypes(String, Boolean) + */ + @Test + public void getRelationshipTypes_shouldReturnEmptyListForNullRelationshipTypeName() { + executeDataSet("org/openmrs/api/include/PersonServiceTest-createRetiredRelationship.xml"); + List relationshipTypes = hibernatePersonDAO.getRelationshipTypes(null, true); + assertNotNull(relationshipTypes); + assertTrue(relationshipTypes.isEmpty(), "Should return an empty list for null relationshipTypeName"); + } + + /** + * @see HibernatePersonDAO#getRelationshipTypes(String, Boolean) + */ + @Test + public void getRelationshipTypes_shouldReturnEmptyListForEmptyRelationshipTypeName() { + executeDataSet("org/openmrs/api/include/PersonServiceTest-createRetiredRelationship.xml"); + List relationshipTypes = hibernatePersonDAO.getRelationshipTypes("", true); + assertNotNull(relationshipTypes); + assertTrue(relationshipTypes.isEmpty(), "Should return an empty list for empty relationshipTypeName"); + } + } diff --git a/api/src/test/java/org/openmrs/api/db/hibernate/MatchModeTest.java b/api/src/test/java/org/openmrs/api/db/hibernate/MatchModeTest.java new file mode 100644 index 000000000000..b341b8cc09a3 --- /dev/null +++ b/api/src/test/java/org/openmrs/api/db/hibernate/MatchModeTest.java @@ -0,0 +1,44 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.api.db.hibernate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class MatchModeTest { + + static Stream data() { + return Arrays.stream(new Object[][]{ + {MatchMode.START, "TEST", true, "test%"}, + {MatchMode.END, "TeST", true, "%test"}, + {MatchMode.ANYWHERE, "test", true, "%test%"}, + {MatchMode.EXACT, "TEst", true, "test"}, + {MatchMode.START, "TEST", false, "TEST%"}, + {MatchMode.END, "TeST", false, "%TeST"}, + {MatchMode.ANYWHERE, "test", false, "%test%"}, + {MatchMode.EXACT, "TEst", false, "TEst"}, + }); + } + + @ParameterizedTest + @MethodSource("data") + public void shouldMatchPatternCorrectly(MatchMode matchMode, String input, boolean caseInsensitive, String expectedPattern) { + if (caseInsensitive) { + assertEquals(expectedPattern, matchMode.toLowerCasePattern(input)); + } else { + assertEquals(expectedPattern, matchMode.toCaseSensitivePattern(input)); + } + } +} diff --git a/api/src/test/java/org/openmrs/api/db/hibernate/PatientSearchCriteriaTest.java b/api/src/test/java/org/openmrs/api/db/hibernate/PatientSearchCriteriaTest.java index 63531d77795c..1fd2e2f45bb9 100644 --- a/api/src/test/java/org/openmrs/api/db/hibernate/PatientSearchCriteriaTest.java +++ b/api/src/test/java/org/openmrs/api/db/hibernate/PatientSearchCriteriaTest.java @@ -18,12 +18,9 @@ import java.util.ArrayList; import java.util.List; -import org.hibernate.Criteria; import org.hibernate.SessionFactory; -import org.hibernate.criterion.MatchMode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openmrs.Patient; import org.openmrs.PatientIdentifierType; import org.openmrs.api.context.Context; import org.openmrs.test.jupiter.BaseContextSensitiveTest; @@ -39,9 +36,8 @@ public class PatientSearchCriteriaTest extends BaseContextSensitiveTest { @BeforeEach public void setUp() { SessionFactory sessionFactory = (SessionFactory) applicationContext.getBean("sessionFactory"); - Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Patient.class); - patientSearchCriteria = new PatientSearchCriteria(sessionFactory, criteria); + patientSearchCriteria = new PatientSearchCriteria(sessionFactory); globalPropertiesTestHelper = new GlobalPropertiesTestHelper(Context.getAdministrationService()); } diff --git a/api/src/test/java/org/openmrs/api/handler/RequiredReasonVoidSaveHandlerTest.java b/api/src/test/java/org/openmrs/api/handler/RequiredReasonVoidSaveHandlerTest.java index 2178e1de3971..b9590ec6297e 100644 --- a/api/src/test/java/org/openmrs/api/handler/RequiredReasonVoidSaveHandlerTest.java +++ b/api/src/test/java/org/openmrs/api/handler/RequiredReasonVoidSaveHandlerTest.java @@ -9,20 +9,20 @@ */ package org.openmrs.api.handler; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.Date; - import org.junit.jupiter.api.Test; import org.openmrs.Encounter; import org.openmrs.Patient; -import org.openmrs.Person; +import org.openmrs.PersonAddress; import org.openmrs.User; import org.openmrs.Voidable; import org.openmrs.api.APIException; import org.openmrs.api.context.Context; import org.openmrs.test.jupiter.BaseContextSensitiveTest; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * Tests for the {@link RequireVoidReasonSaveHandler} class. */ @@ -77,10 +77,9 @@ public void handle_shouldNotThrowExceptionIfVoidReasonIsNotBlank() { */ @Test public void handle_shouldNotThrowExceptionIfVoidReasonIsNullForUnsupportedTypes() { - Person p = Context.getPersonService().getPerson(1); - p.setVoided(true); - p.setVoidReason(null); - p.setVoidReason("voidReason"); - Context.getPersonService().savePerson(p); + PersonAddress pa = Context.getPersonService().getPersonAddressByUuid("3350d0b5-821c-4e5e-ad1d-a9bce331e118"); + pa.setVoided(true); + pa.setVoidReason(null); + Context.getPersonService().savePersonAddress(pa); } } diff --git a/api/src/test/java/org/openmrs/api/impl/ConceptServiceImplTest.java b/api/src/test/java/org/openmrs/api/impl/ConceptServiceImplTest.java index c68d1f3ca542..815ccb6bd10b 100644 --- a/api/src/test/java/org/openmrs/api/impl/ConceptServiceImplTest.java +++ b/api/src/test/java/org/openmrs/api/impl/ConceptServiceImplTest.java @@ -41,6 +41,7 @@ import org.openmrs.ConceptSet; import org.openmrs.ConceptSource; import org.openmrs.Drug; +import org.openmrs.DrugReferenceMap; import org.openmrs.api.APIException; import org.openmrs.api.ConceptNameType; import org.openmrs.api.ConceptService; @@ -344,6 +345,31 @@ public void saveDrug_shouldUpdateDrugAlreadyExistingInDatabase() { conceptService.saveDrug(savedDrug); assertTrue(conceptService.getDrug(savedDrug.getDrugId()).getCombination()); } + + /** + * @see ConceptServiceImpl#saveDrug(Drug) + */ + @Test + public void saveDrug_shouldSaveNewDrugReferenceMap() { + Drug drug = new Drug(); + Concept concept = new Concept(); + concept.addName(new ConceptName("Concept", new Locale("en", "US"))); + concept.addDescription(new ConceptDescription("Description", new Locale("en", "US"))); + concept.setConceptClass(new ConceptClass(1)); + concept.setDatatype(new ConceptDatatype(1)); + Concept savedConcept = conceptService.saveConcept(concept); + drug.setConcept(savedConcept); + drug.setName("Example Drug"); + ConceptMapType sameAs = conceptService.getConceptMapTypeByUuid(ConceptMapType.SAME_AS_MAP_TYPE_UUID); + ConceptSource snomedCt = conceptService.getConceptSourceByName("SNOMED CT"); + DrugReferenceMap map = new DrugReferenceMap(); + map.setDrug(drug); + map.setConceptMapType(sameAs); + map.setConceptReferenceTerm(new ConceptReferenceTerm(snomedCt, "example", "")); + drug.addDrugReferenceMap(map); + drug = conceptService.saveDrug(drug); + assertEquals(1, conceptService.getDrug(drug.getDrugId()).getDrugReferenceMaps().size()); + } /** * @see ConceptServiceImpl#purgeConcept(Concept) @@ -489,7 +515,7 @@ public void unretireDrug_shouldNotChangeAttributesOfDrugThatIsAlreadyNotRetired( */ @Test public void getAllConceptClasses_shouldReturnAListOfAllConceptClasses() { - int resultSize = 20; + int resultSize = 21; List conceptClasses = conceptService.getAllConceptClasses(); assertEquals(resultSize, conceptClasses.size()); } @@ -499,7 +525,7 @@ public void getAllConceptClasses_shouldReturnAListOfAllConceptClasses() { */ @Test public void getAllConceptClasses_shouldReturnAllConceptClassesIncludingRetiredOnesWhenGivenTrue() { - int resultSizeWhenTrue = 20; + int resultSizeWhenTrue = 21; List conceptClasses = conceptService.getAllConceptClasses(true); assertEquals(resultSizeWhenTrue, conceptClasses.size()); } @@ -631,7 +657,7 @@ public void getDrugsByIngredient_shouldRaiseExceptionIfNoConceptIsGiven() { */ @Test public void getAllConceptProposals_shouldReturnAllConceptProposalsIncludingRetiredOnesWhenGivenTrue() { - int matchedConceptProposals = 2; + int matchedConceptProposals = 3; List conceptProposals = conceptService.getAllConceptProposals(true); assertEquals(matchedConceptProposals, conceptProposals.size()); } diff --git a/api/src/test/java/org/openmrs/api/impl/ConditionServiceImplTest.java b/api/src/test/java/org/openmrs/api/impl/ConditionServiceImplTest.java index 7b70448df5d0..12fa597e3955 100644 --- a/api/src/test/java/org/openmrs/api/impl/ConditionServiceImplTest.java +++ b/api/src/test/java/org/openmrs/api/impl/ConditionServiceImplTest.java @@ -9,19 +9,6 @@ */ package org.openmrs.api.impl; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.List; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openmrs.CodedOrFreeText; @@ -30,12 +17,31 @@ import org.openmrs.ConditionVerificationStatus; import org.openmrs.Encounter; import org.openmrs.Patient; +import org.openmrs.api.ConceptService; import org.openmrs.api.ConditionService; +import org.openmrs.api.EncounterService; import org.openmrs.api.PatientService; import org.openmrs.api.context.Context; import org.openmrs.test.jupiter.BaseContextSensitiveTest; import org.springframework.beans.factory.annotation.Autowired; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * Unit tests for methods that are specific to the {@link ConditionServiceImpl}. General tests that * would span implementations should go on the {@link ConditionService}. @@ -53,6 +59,12 @@ public class ConditionServiceImplTest extends BaseContextSensitiveTest { @Autowired private PatientService patientService; + + @Autowired + private EncounterService encounterService; + + @Autowired + private ConceptService conceptService; @BeforeEach public void setup (){ @@ -88,37 +100,121 @@ public void saveCondition_shouldSaveNewCondition() { @Test public void saveCondition_shouldReplaceExistingCondition() { + // setup - Condition condition = new Condition(); - condition.setCondition(new CodedOrFreeText()); - condition.setClinicalStatus(ConditionClinicalStatus.INACTIVE); - condition.setVerificationStatus(ConditionVerificationStatus.CONFIRMED); - condition.setUuid(EXISTING_CONDITION_UUID); - condition.setPatient(new Patient(2)); + Condition condition = conditionService.getConditionByUuid(EXISTING_CONDITION_UUID); + Integer oldConditionId = condition.getConditionId(); // perform - Condition newCondition = conditionService.saveCondition(condition); + condition.setClinicalStatus(ConditionClinicalStatus.INACTIVE); + condition = conditionService.saveCondition(condition); + Integer newConditionId = condition.getConditionId(); // verify - Condition oldCondition = conditionService.getConditionByUuid(EXISTING_CONDITION_UUID); + assertNotEquals(oldConditionId, newConditionId); + + // existing condition should be unchanged, but voided + Condition oldCondition = conditionService.getCondition(oldConditionId); + assertEquals(EXISTING_CONDITION_UUID, oldCondition.getUuid()); + assertEquals(ConditionClinicalStatus.ACTIVE, oldCondition.getClinicalStatus()); assertTrue(oldCondition.getVoided()); + assertNotNull(oldCondition.getOnsetDate()); + assertNull(oldCondition.getEndDate()); + + // new condition should reflect changed existing condition and have it as a previous version + Condition newCondition = conditionService.getCondition(newConditionId); + assertNotEquals(EXISTING_CONDITION_UUID, newCondition.getUuid()); assertEquals(newCondition.getPreviousVersion(), oldCondition); assertEquals(newCondition.getClinicalStatus(), ConditionClinicalStatus.INACTIVE); + assertEquals(oldCondition.getOnsetDate().getTime(), newCondition.getOnsetDate().getTime()); + assertNull(newCondition.getEndDate()); + } + + @Test + public void saveCondition_shouldRetainPropertiesOfCondition() throws Exception { + DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + // setup + Condition c = new Condition(); + CodedOrFreeText codedOrFreeText = new CodedOrFreeText(); + codedOrFreeText.setCoded(conceptService.getConcept(11)); + codedOrFreeText.setSpecificName(conceptService.getConceptName(2460)); + c.setCondition(codedOrFreeText); + c.setClinicalStatus(ConditionClinicalStatus.INACTIVE); + c.setVerificationStatus(ConditionVerificationStatus.CONFIRMED); + c.setAdditionalDetail("Additional information"); + c.setOnsetDate(df.parse("2021-06-30")); + c.setEndDate(df.parse("2022-01-25")); + c.setEndReason("This is an end reason"); + c.setPatient(patientService.getPatient(2)); + c.setEncounter(encounterService.getEncounter(6)); + c.setFormNamespaceAndPath("form1/namespace2/path3"); + c = conditionService.saveCondition(c); + Integer conditionId1 = c.getConditionId(); - // asserting previous behaviour using onset and end date no longer has any effect - assertNull(newCondition.getOnsetDate()); - assertNull(oldCondition.getEndDate()); + // edit + c.setAdditionalDetail("Edited info"); + c = conditionService.saveCondition(c); + Integer conditionId2 = c.getConditionId(); + + // verify + assertNotEquals(conditionId1, conditionId2); + Condition c1 = conditionService.getCondition(conditionId1); + Condition c2 = conditionService.getCondition(conditionId2); + assertNotEquals(c1.getUuid(), c2.getUuid()); + assertEquals(11, c2.getCondition().getCoded().getConceptId()); + assertNull(c2.getCondition().getNonCoded()); + assertEquals(2460, c2.getCondition().getSpecificName().getConceptNameId()); + assertEquals(ConditionClinicalStatus.INACTIVE, c2.getClinicalStatus()); + assertEquals(ConditionVerificationStatus.CONFIRMED, c2.getVerificationStatus()); + assertEquals("Additional information", c1.getAdditionalDetail()); + assertEquals("Edited info", c2.getAdditionalDetail()); + assertEquals("2021-06-30", df.format(c2.getOnsetDate())); + assertEquals("2022-01-25", df.format(c2.getEndDate())); + // assertEquals("This is an end reason", c2.getEndReason()); // End reason is not persisted in the DB + assertEquals(2, c2.getPatient().getPatientId()); + assertEquals(6, c2.getEncounter().getEncounterId()); + assertEquals("form1/namespace2/path3", c2.getFormNamespaceAndPath()); + assertEquals(c1, c2.getPreviousVersion()); + assertTrue(c1.getVoided()); + assertFalse(c2.getVoided()); + + // edit again + codedOrFreeText = new CodedOrFreeText(); + codedOrFreeText.setNonCoded("Non-coded condition"); + c.setCondition(codedOrFreeText); + c = conditionService.saveCondition(c); + Integer conditionId3 = c.getConditionId(); + + // verify again + Condition c3 = conditionService.getCondition(conditionId3); + assertEquals(c3.getPreviousVersion(), c2); + assertNull(c3.getCondition().getCoded()); + assertNull(c3.getCondition().getSpecificName()); + assertEquals("Non-coded condition", c3.getCondition().getNonCoded()); + } + + @Test + public void saveCondition_shouldNotVoidAndRecreateIfUnchanged() throws Exception { + Condition condition = conditionService.getConditionByUuid(EXISTING_CONDITION_UUID); + Integer conditionId = condition.getConditionId(); + int startingNum = conditionService.getAllConditions(condition.getPatient()).size(); + condition = conditionService.saveCondition(condition); + int endingNum = conditionService.getAllConditions(condition.getPatient()).size(); + assertEquals(startingNum, endingNum); + assertEquals(EXISTING_CONDITION_UUID, condition.getUuid()); + assertEquals(conditionId, condition.getConditionId()); } @Test - public void saveCondition_shouldVoidExistingCondition() { + public void saveCondition_shouldVoidExistingConditionAndNotCreateNewCondition() { // setup - Condition condition = new Condition(); - condition.setUuid(EXISTING_CONDITION_UUID); - condition.setVoided(true); - condition.setVoidReason("Voided by a test"); + Condition condition = conditionService.getConditionByUuid(EXISTING_CONDITION_UUID); + Patient patient = condition.getPatient(); + int startingNum = conditionService.getAllConditions(patient).size(); // perform + condition.setVoided(true); + condition.setVoidReason("Voided by a test"); Condition voidedCondition = conditionService.saveCondition(condition); // verify @@ -129,85 +225,38 @@ public void saveCondition_shouldVoidExistingCondition() { assertEquals(voidedCondition.getId(), oldCondition.getId()); assertTrue(oldCondition.getVoided()); assertEquals(voidedCondition.getVoidReason(), oldCondition.getVoidReason()); + + int endingNum = conditionService.getAllConditions(patient).size(); + assertEquals(startingNum, endingNum+1); } @Test - public void saveCondition_shouldNotChangeAnyOtherFieldsWhenVoidingCondition() { + public void saveCondition_shouldUnvoidExistingVoidedCondition() { // setup - Condition condition = new Condition(); - condition.setUuid(EXISTING_CONDITION_UUID); + Condition condition = conditionService.getConditionByUuid(EXISTING_CONDITION_UUID); + Patient patient = condition.getPatient(); condition.setVoided(true); condition.setVoidReason("Voided by a test"); - condition.setPatient(new Patient(8)); + condition = conditionService.saveCondition(condition); + assertTrue(condition.getVoided()); + int startingNum = conditionService.getAllConditions(patient).size(); // perform - Condition voidedCondition = conditionService.saveCondition(condition); - - // verify - assertTrue(voidedCondition.getVoided()); - assertEquals("Voided by a test", voidedCondition.getVoidReason()); - assertEquals(voidedCondition.getPatient().getId(), 2); - } - - @Test - public void saveCondition_shouldUnvoidExistingVoidedCondition() { - // setup - Context.getConditionService().voidCondition(conditionService.getConditionByUuid(EXISTING_CONDITION_UUID), "Voided for test"); - assertTrue(conditionService.getConditionByUuid(EXISTING_CONDITION_UUID).getVoided()); - - Condition condition = new Condition(); condition.setVoided(false); - condition.setUuid(EXISTING_CONDITION_UUID); - - // perform Condition unvoidedCondition = conditionService.saveCondition(condition); // verify assertFalse(unvoidedCondition.getVoided()); + assertNull(unvoidedCondition.getDateVoided()); + assertNull(unvoidedCondition.getVoidedBy()); assertNull(unvoidedCondition.getVoidReason()); Condition oldCondition = conditionService.getConditionByUuid(EXISTING_CONDITION_UUID); - assertEquals(unvoidedCondition.getId(), oldCondition.getId()); - assertFalse(oldCondition.getVoided()); - assertEquals(unvoidedCondition.getVoidReason(), oldCondition.getVoidReason()); - } - - @Test - public void saveCondition_shouldNotChangeAnyOtherFieldsWhenUnvoidingCondition() { - // setup - Context.getConditionService().voidCondition(conditionService.getConditionByUuid(EXISTING_CONDITION_UUID), "Voided for test"); - assertTrue(conditionService.getConditionByUuid(EXISTING_CONDITION_UUID).getVoided()); - - Condition condition = new Condition(); - condition.setVoided(false); - condition.setUuid(EXISTING_CONDITION_UUID); - condition.setPatient(new Patient(8)); - - // perform - Condition unvoidedCondition = conditionService.saveCondition(condition); - - // verify - assertFalse(unvoidedCondition.getVoided()); - assertEquals(unvoidedCondition.getPatient().getId(), 2); - } - - @Test - public void saveCondition_shouldNotUpdateAVoidedCondition() { - // setup - Context.getConditionService().voidCondition(conditionService.getConditionByUuid(EXISTING_CONDITION_UUID), "Voided for test"); - assertTrue(conditionService.getConditionByUuid(EXISTING_CONDITION_UUID).getVoided()); - - Condition condition = new Condition(); - condition.setVoided(true); - condition.setUuid(EXISTING_CONDITION_UUID); - condition.setPatient(new Patient(8)); - - // perform - Condition voidedCondition = conditionService.saveCondition(condition); - - // verify - assertTrue(voidedCondition.getVoided()); - assertEquals(voidedCondition.getPatient().getId(), 2); + assertNotEquals(unvoidedCondition.getId(), oldCondition.getId()); + assertTrue(oldCondition.getVoided()); + assertEquals("Voided by a test", oldCondition.getVoidReason()); + int endingNum = conditionService.getAllConditions(patient).size(); + assertEquals(startingNum, endingNum-1); } /** @@ -223,12 +272,18 @@ public void saveCondition_shouldSaveConditionAssociatedWithAnEncounter() { condition.setClinicalStatus(ConditionClinicalStatus.ACTIVE); // replay - condition.setEncounter(new Encounter(2039)); + condition.setEncounter(encounterService.getEncounter(2039)); conditionService.saveCondition(condition); // verify Condition savedCondition = conditionService.getConditionByUuid(uuid); assertEquals(Integer.valueOf(2039), savedCondition.getEncounter().getId()); + + // edit and verify edit + savedCondition.setOnsetDate(new Date()); + Condition editedCondition = conditionService.saveCondition(condition); + assertNotNull(editedCondition.getEncounter()); + assertEquals(savedCondition.getEncounter(), editedCondition.getEncounter()); } /** @@ -255,6 +310,12 @@ public void saveCondition_shouldSaveConditionWithFormField(){ // Validate test assertEquals(ns + FORM_NAMESPACE_PATH_SEPARATOR + path, savedCondition.getFormNamespaceAndPath()); + + // edit and verify edit + savedCondition.setOnsetDate(new Date()); + Condition editedCondition = conditionService.saveCondition(condition); + assertNotNull(editedCondition.getFormNamespaceAndPath()); + assertEquals(savedCondition.getFormNamespaceAndPath(), editedCondition.getFormNamespaceAndPath()); } /** @@ -292,9 +353,7 @@ public void getCondition_shouldFindConditionGivenValidId() { @Test public void getActiveConditions_shouldGetActiveConditions() { List activeConditions = conditionService.getActiveConditions(patientService.getPatient(2)); - assertThat(activeConditions, hasSize(1)); - assertEquals("2cc6880e-2c46-11e4-9138-a6c5e4d20fb7", activeConditions.get(0).getUuid()); } @@ -302,14 +361,13 @@ public void getActiveConditions_shouldGetActiveConditions() { * @see ConditionService#getAllConditions(Patient) */ @Test - public void getAllConditions_shouldGetAllConditions() { - List conditions = conditionService.getAllConditions(patientService.getPatient(2)); - - assertThat(conditions, hasSize(3)); - - assertThat(conditions.get(0).getUuid(), equalTo("2cb6880e-2cd6-11e4-9138-a6c5e4d20fb7")); - assertThat(conditions.get(1).getUuid(), equalTo("2cc6880e-2c46-15e4-9038-a6c5e4d22fb7")); - assertThat(conditions.get(2).getUuid(), equalTo("2cc6880e-2c46-11e4-9138-a6c5e4d20fb7")); + public void getAllConditions_shouldGetAllUnvoidedConditions() { + Patient patient = patientService.getPatient(2); + List conditions = conditionService.getAllConditions(patient); + assertThat(conditions, hasSize(2)); + // Condition with uuid 2cb6880e-2cd6-11e4-9138-a6c5e4d20fb7 is voided + assertThat(conditions.get(0).getUuid(), equalTo("2cc6880e-2c46-15e4-9038-a6c5e4d22fb7")); + assertThat(conditions.get(1).getUuid(), equalTo("2cc6880e-2c46-11e4-9138-a6c5e4d20fb7")); } /** @@ -359,7 +417,7 @@ public void unvoidCondition_shouldUnvoidConditionSuccessfully(){ assertTrue(voidedCondition.getVoided()); assertNotNull(voidedCondition.getVoidReason()); assertNotNull(voidedCondition.getDateVoided()); - assertEquals(new Integer(1), voidedCondition.getVoidedBy().getUserId()); + assertEquals(1, voidedCondition.getVoidedBy().getUserId()); Condition unVoidedCondition = conditionService.unvoidCondition(voidedCondition); diff --git a/api/src/test/java/org/openmrs/api/impl/PatientServiceImplTest.java b/api/src/test/java/org/openmrs/api/impl/PatientServiceImplTest.java index 7b09867a0e5c..486bc8db0586 100644 --- a/api/src/test/java/org/openmrs/api/impl/PatientServiceImplTest.java +++ b/api/src/test/java/org/openmrs/api/impl/PatientServiceImplTest.java @@ -33,7 +33,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; import org.mockito.Mock; import org.openmrs.Concept; import org.openmrs.Location; @@ -284,7 +283,7 @@ public void processDeath_shouldMapValuesAndSavePatient() throws Exception { final Date dateDied = new Date(); final Concept causeOfDeath = new Concept(2); - when(conceptService.getConcept((String)Matchers.any())).thenReturn(new Concept()); + when(conceptService.getConcept((String)any())).thenReturn(new Concept()); when(locationService.getDefaultLocation()).thenReturn(new Location()); UserContext userContext = mock(UserContext.class); diff --git a/api/src/test/java/org/openmrs/layout/name/NameTemplateTest.java b/api/src/test/java/org/openmrs/layout/name/NameTemplateTest.java index 171719ec0856..7a4dc6b1b8e4 100644 --- a/api/src/test/java/org/openmrs/layout/name/NameTemplateTest.java +++ b/api/src/test/java/org/openmrs/layout/name/NameTemplateTest.java @@ -20,18 +20,22 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.openmrs.GlobalProperty; import org.openmrs.PersonName; +import org.openmrs.api.context.Context; import org.openmrs.test.jupiter.BaseContextSensitiveTest; +import org.openmrs.util.OpenmrsConstants; public class NameTemplateTest extends BaseContextSensitiveTest { + private final String NAME_TEMPLATE_GP_DATASET_PATH = "src/test/resources/org/openmrs/include/nameSupportTestDataSet.xml"; private NameSupport nameSupport; @BeforeEach public void setup() { nameSupport = NameSupport.getInstance(); nameSupport.setSpecialTokens(Arrays.asList("prefix", "givenName", "middleName", "familyNamePrefix", - "familyNameSuffix", "familyName2", "familyName", "degree")); + "familyNameSuffix", "familyName2", "familyName", "degree")); } @Test @@ -97,5 +101,23 @@ public void shouldProperlyFormatNameWithNonTokens() { assertEquals("Goodrich, Mark \"Blue State\"", nameTemplate.format(personName)); } + + @Test + public void shouldUseNameTemplateConfiguredViaGlobalProperties() { + // setup + executeDataSet(NAME_TEMPLATE_GP_DATASET_PATH); + Context.getAdministrationService().saveGlobalProperty(new GlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_LAYOUT_NAME_FORMAT, "customXmlTemplate")); + + PersonName personName = new PersonName(); + personName.setGivenName("Moses"); + personName.setMiddleName("Tusha"); + personName.setFamilyName("Mujuzi"); + + // replay + NameTemplate nameTemplate = NameSupport.getInstance().getDefaultLayoutTemplate(); + + // verify + assertEquals("Moses Mujuzi", nameTemplate.format(personName)); + } } diff --git a/api/src/test/java/org/openmrs/liquibase/OpenmrsClassLoaderResourceAccessorTest.java b/api/src/test/java/org/openmrs/liquibase/OpenmrsClassLoaderResourceAccessorTest.java new file mode 100644 index 000000000000..43dac8341292 --- /dev/null +++ b/api/src/test/java/org/openmrs/liquibase/OpenmrsClassLoaderResourceAccessorTest.java @@ -0,0 +1,60 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.liquibase; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.Collections; + +import liquibase.resource.InputStreamList; +import org.junit.jupiter.api.Test; +import org.openmrs.liquibase.OpenmrsClassLoaderResourceAccessor; +import org.openmrs.util.OpenmrsClassLoader; + +public class OpenmrsClassLoaderResourceAccessorTest { + + @Test + public void shouldGetSingleResourceAsStream() throws Exception { + ClassLoader classLoader = mock(ClassLoader.class); + + when(classLoader.getResources(any())) + .thenReturn(OpenmrsClassLoader.getSystemClassLoader().getResources("TestingApplicationContext.xml")); + + + OpenmrsClassLoaderResourceAccessor classLoaderFileOpener = new OpenmrsClassLoaderResourceAccessor(classLoader); + try (InputStreamList inputStreamSet = classLoaderFileOpener.openStreams(null, "some path")) { + assertEquals(1, inputStreamSet.size()); + } + } + + @Test + public void shouldGetNoResourceAsStream() throws Exception { + ClassLoader classLoader = mock(ClassLoader.class); + + when(classLoader.getResources(any())) + .thenReturn(Collections.emptyEnumeration()); + + + try (OpenmrsClassLoaderResourceAccessor classLoaderFileOpener = new OpenmrsClassLoaderResourceAccessor(classLoader); + InputStreamList inputStreamSet = classLoaderFileOpener.openStreams(null, "")){ + assertThat(inputStreamSet.size(), is(0)); + } + } +} diff --git a/api/src/test/java/org/openmrs/module/ModuleExtensionsTest.java b/api/src/test/java/org/openmrs/module/ModuleExtensionsTest.java index 73c25f9fce1f..cb418b60c7a5 100644 --- a/api/src/test/java/org/openmrs/module/ModuleExtensionsTest.java +++ b/api/src/test/java/org/openmrs/module/ModuleExtensionsTest.java @@ -54,7 +54,7 @@ public void before() { public void after() { // needed so other tests which rely on no ModuleClassLoaderFound // are not affected by tests registering one - ModuleFactory.moduleClassLoaders = null; + ModuleFactory.moduleClassLoaders.invalidateAll(); } @Test @@ -80,7 +80,7 @@ public void getExtensions_shouldNotExpandIfNoModuleClassloaderIsFound() { extensionNames.put(EXTENSION_POINT_ID_PATIENT_DASHBOARD, AccessibleExtension.class.getName()); module.setExtensionNames(extensionNames); - ModuleFactory.moduleClassLoaders = null; + ModuleFactory.moduleClassLoaders.invalidateAll(); assertThat(module.getExtensions(), is(equalTo(Collections.EMPTY_LIST))); } diff --git a/api/src/test/java/org/openmrs/module/ModuleFileParserTest.java b/api/src/test/java/org/openmrs/module/ModuleFileParserTest.java index 453d808cb82d..6a1096370e86 100644 --- a/api/src/test/java/org/openmrs/module/ModuleFileParserTest.java +++ b/api/src/test/java/org/openmrs/module/ModuleFileParserTest.java @@ -70,35 +70,32 @@ public static void setUp() throws ParserConfigurationException { @Test public void moduleFileParser_shouldFailCreatingParserFromFileIfGivenNull() { - expectModuleExceptionWithTranslatedMessage(() -> new ModuleFileParser((File) null), "Module.error.fileCannotBeNull"); } @Test public void moduleFileParser_shouldFailCreatingParserFromFileIfNotEndingInOmod() { - expectModuleExceptionWithTranslatedMessage(() -> new ModuleFileParser(new File("reporting.jar")), "Module.error.invalidFileExtension"); } @Test public void moduleFileParser_shouldFailCreatingParserFromFileIfInputStreamClosed() throws IOException { - File moduleFile = new File(getClass().getClassLoader().getResource(LOGIC_MODULE_PATH).getPath()); - InputStream inputStream = new FileInputStream(moduleFile); - inputStream.close(); - - expectModuleExceptionWithTranslatedMessage(() -> new ModuleFileParser(inputStream), "Module.error.cannotCreateFile"); + try (InputStream inputStream = new FileInputStream(moduleFile)) { + inputStream.close(); + expectModuleExceptionWithTranslatedMessage(() -> new ModuleFileParser(inputStream), "Module.error.cannotCreateFile"); + } } @Test public void parse_shouldParseValidXmlConfigCreatedFromInputStream() throws IOException { - File moduleFile = new File(getClass().getClassLoader().getResource(LOGIC_MODULE_PATH).getPath()); - ModuleFileParser parser = new ModuleFileParser(new FileInputStream(moduleFile)); - - Module module = parser.parse(); + Module module; + try (FileInputStream moduleFileInputStream = new FileInputStream(moduleFile)) { + module = new ModuleFileParser().parse(moduleFileInputStream); + } assertThat(module.getModuleId(), is("logic")); assertThat(module.getVersion(), is("0.2")); @@ -122,21 +119,18 @@ public void parse_shouldFailIfModuleHasConfigInvalidConfigVersion() throws Excep String invalidConfigVersion = "0.0.1"; String expectedMessage = messageSourceService .getMessage("Module.error.invalidConfigVersion", - new Object[] { invalidConfigVersion, "1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6" }, Context.getLocale()); + new Object[] { invalidConfigVersion, "1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7" }, Context.getLocale()); Document configXml = documentBuilder.newDocument(); Element root = configXml.createElement("module"); configXml.appendChild(root); configXml.getDocumentElement().setAttribute("configVersion", invalidConfigVersion); - ModuleFileParser parser = new ModuleFileParser(writeConfigXmlToFile(configXml)); - - expectModuleExceptionWithMessage(() -> parser.parse(), expectedMessage); + expectModuleExceptionWithMessage(() -> new ModuleFileParser().parse(writeConfigXmlToFile(configXml)), expectedMessage); } @Test public void parse_shouldParseValidLogicModuleFromFile() { - File moduleFile = new File(getClass().getClassLoader().getResource(LOGIC_MODULE_PATH).getPath()); ModuleFileParser parser = new ModuleFileParser(Context.getMessageSourceService()); diff --git a/api/src/test/java/org/openmrs/module/ModuleFileParserUnitTest.java b/api/src/test/java/org/openmrs/module/ModuleFileParserUnitTest.java index a3c56652f513..9a3fa786c2a8 100644 --- a/api/src/test/java/org/openmrs/module/ModuleFileParserUnitTest.java +++ b/api/src/test/java/org/openmrs/module/ModuleFileParserUnitTest.java @@ -14,6 +14,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.core.StringStartsWith.startsWith; @@ -91,7 +92,7 @@ public void setUpModuleFileParser() { @AfterEach public void after() { // needed so other are not affected by tests registering a ModuleClassLoader - ModuleFactory.moduleClassLoaders = null; + ModuleFactory.moduleClassLoaders.invalidateAll(); } @Test @@ -532,6 +533,107 @@ public void parse_shouldParseAwareOfModulesContainingMultipleChildren() throws I hasItems("org.openmrs.module.serialization.xstream", "org.openmrs.module.legacyui")); } + @Test + public void parse_shouldParseStartBeforeModulesWithoutChildren() throws IOException { + Document config = buildOnValidConfigXml() + .withStartBeforeModules() + .build(); + + Module module = parser.parse(writeConfigXmlToFile(config)); + + assertThat(module.getStartBeforeModules(), is(equalTo(Collections.EMPTY_LIST))); + } + + @Test + public void parse_shouldParseStartBeforeModulesOnlyContainingText() throws IOException { + Document config = buildOnValidConfigXml() + .withTextNode("start_before_modules", "will be ignored") + .build(); + + Module module = parser.parse(writeConfigXmlToFile(config)); + + assertThat(module.getStartBeforeModules(), is(equalTo(Collections.EMPTY_LIST))); + } + + @Test + public void parse_shouldParseStartBeforeModulesContainingDuplicatesAndKeepOnlyOneModule() + throws IOException { + + Document config = buildOnValidConfigXml() + .withStartBeforeModules( + "org.openmrs.module.serialization.xstream", + "org.openmrs.module.serialization.xstream" + ) + .build(); + + Module module = parser.parse(writeConfigXmlToFile(config)); + + assertThat(module.getStartBeforeModules(), hasSize(1)); + assertThat(module.getStartBeforeModules(), hasItems("org.openmrs.module.serialization.xstream")); + } + + @Test + public void parse_shouldParseStartBeforeModulesContainingMultipleChildren() throws IOException { + Document config = buildOnValidConfigXml() + .withStartBeforeModules( + "org.openmrs.module.serialization.xstream", + "org.openmrs.module.legacyui" + ) + .build(); + + Module module = parser.parse(writeConfigXmlToFile(config)); + + assertThat(module.getStartBeforeModules(), hasSize(2)); + assertThat(module.getStartBeforeModules(), + hasItems("org.openmrs.module.serialization.xstream", "org.openmrs.module.legacyui")); + } + + @Test + public void parse_shouldParseStartBeforeModulesContainingModuleChildren() throws IOException { + Document config = buildOnValidConfigXml().build(); + + Element startBeforeModules = config.createElement("start_before_modules"); + for (String module : new String[] { "org.openmrs.module.serialization.xstream", "org.openmrs.module.legacyui" }) { + Element startBeforeModule = config.createElement("module"); + startBeforeModule.setTextContent(module); + startBeforeModules.appendChild(startBeforeModule); + } + config.getDocumentElement().appendChild(startBeforeModules); + + Module module = parser.parse(writeConfigXmlToFile(config)); + + assertThat(module.getStartBeforeModules(), hasSize(2)); + assertThat(module.getStartBeforeModules(), + hasItems("org.openmrs.module.serialization.xstream", "org.openmrs.module.legacyui")); + } + + @Test + public void parse_shouldParseStartBeforeModulesContainingMixedChildren() throws IOException { + Document config = buildOnValidConfigXml().build(); + + Element startBeforeModules = config.createElement("start_before_modules"); + for (String module : new String[] { "org.openmrs.module.xforms", "org.openmrs.module.idgen" }) { + Element startBeforeModule = config.createElement("start_before_module"); + startBeforeModule.setTextContent(module); + startBeforeModules.appendChild(startBeforeModule); + } + + for (String module : new String[] { "org.openmrs.module.serialization.xstream", "org.openmrs.module.legacyui" }) { + Element startBeforeModule = config.createElement("module"); + startBeforeModule.setTextContent(module); + startBeforeModules.appendChild(startBeforeModule); + } + + config.getDocumentElement().appendChild(startBeforeModules); + + Module module = parser.parse(writeConfigXmlToFile(config)); + + assertThat(module.getStartBeforeModules(), hasSize(4)); + assertThat(module.getStartBeforeModules(), + hasItems("org.openmrs.module.serialization.xstream", "org.openmrs.module.legacyui", + "org.openmrs.module.xforms", "org.openmrs.module.idgen")); + } + @Test public void parse_shouldParseExtensions() throws IOException { @@ -700,9 +802,9 @@ public void parse_shouldParseGlobalProperty() throws IOException { GlobalProperty gp2 = new GlobalProperty("report.validateInput", "2", "to validate input", RegexValidatedTextDatatype.class, "^\\d+$"); Document config = buildOnValidConfigXml() - .withGlobalProperty(gp1.getProperty(), gp1.getPropertyValue(), gp1.getDescription(), null, null) + .withGlobalProperty(gp1.getProperty(), gp1.getPropertyValue(), gp1.getDescription(), null, null, null, null, null) .withGlobalProperty(gp2.getProperty(), gp2.getPropertyValue(), gp2.getDescription(), gp2.getDatatypeClassname(), - gp2.getDatatypeConfig()) + gp2.getDatatypeConfig(), null, null, null) .build(); Module module = parser.parse(writeConfigXmlToFile(config)); @@ -724,7 +826,8 @@ public void parse_shouldParseGlobalProperty() throws IOException { public void parse_shouldParseGlobalPropertyAndTrimWhitespacesFromDescription() throws IOException { Document config = buildOnValidConfigXml() - .withGlobalProperty("report.deleteReportsAgeInHours", "72", " \n\t delete reports after\t hours ", null, null) + .withGlobalProperty("report.deleteReportsAgeInHours", "72", " \n\t delete reports after\t hours ", + null, null, null, null, null) .build(); Module module = parser.parse(writeConfigXmlToFile(config)); @@ -737,7 +840,8 @@ public void parse_shouldParseGlobalPropertyAndTrimWhitespacesFromDescription() t public void parse_shouldParseGlobalPropertyWithoutDescriptionElement() throws IOException { Document config = buildOnValidConfigXml() - .withGlobalProperty("report.deleteReportsAgeInHours", "72", null, null, null) + .withGlobalProperty("report.deleteReportsAgeInHours", "72", null, + null, null, null, null, null) .build(); Module module = parser.parse(writeConfigXmlToFile(config)); @@ -752,7 +856,8 @@ public void parse_shouldParseGlobalPropertyContainingElementsNotIncludedInGlobal GlobalProperty gp1 = new GlobalProperty("report.deleteReportsAgeInHours", "72", "delete reports after"); Document config = buildOnValidConfigXml() - .withGlobalProperty(gp1.getProperty(), gp1.getPropertyValue(), gp1.getDescription(), null, null) + .withGlobalProperty(gp1.getProperty(), gp1.getPropertyValue(), gp1.getDescription(), null, + null, null, null, null) .build(); config.getElementsByTagName("globalProperty").item(0).appendChild(config.createElement("ignoreMe")); @@ -791,7 +896,8 @@ public void parse_shouldIgnoreGlobalPropertyOnlyContainingText() throws IOExcept public void parse_shouldIgnoreGlobalPropertyWithoutPropertyElement() throws IOException { Document config = buildOnValidConfigXml() - .withGlobalProperty(null, "72", "some", null, null) + .withGlobalProperty(null, "72", "some", null, null, + null, null, null) .build(); Module module = parser.parse(writeConfigXmlToFile(config)); @@ -803,7 +909,8 @@ public void parse_shouldIgnoreGlobalPropertyWithoutPropertyElement() throws IOEx public void parse_shouldIgnoreGlobalPropertyWithEmptyProperty() throws IOException { Document config = buildOnValidConfigXml() - .withGlobalProperty(" ", "72", "some", null, null) + .withGlobalProperty(" ", "72", "some", null, null, + null, null, null) .build(); Module module = parser.parse(writeConfigXmlToFile(config)); @@ -815,7 +922,8 @@ public void parse_shouldIgnoreGlobalPropertyWithEmptyProperty() throws IOExcepti public void parse_shouldIgnoreGlobalPropertyWithDatatypeClassThatIsNotSubclassingCustomDatatype() throws IOException { Document config = buildOnValidConfigXml() - .withGlobalProperty("report.deleteReportsAgeInHours", "72", "some", "java.lang.String", null) + .withGlobalProperty("report.deleteReportsAgeInHours", "72", "some", + "java.lang.String", null, null, null, null) .build(); Module module = parser.parse(writeConfigXmlToFile(config)); @@ -827,7 +935,8 @@ public void parse_shouldIgnoreGlobalPropertyWithDatatypeClassThatIsNotSubclassin public void parse_shouldIgnoreGlobalPropertyWithDatatypeClassThatIsNotFound() throws IOException { Document config = buildOnValidConfigXml() - .withGlobalProperty("report.deleteReportsAgeInHours", "72", "some", "String", null) + .withGlobalProperty("report.deleteReportsAgeInHours", "72", "some", + "String", null, null, null, null) .build(); Module module = parser.parse(writeConfigXmlToFile(config)); @@ -1165,6 +1274,24 @@ public void parse_shouldIgnoreAdviceWithoutPoint() throws IOException { assertThat(module.getAdvicePoints(), is(equalTo(Collections.EMPTY_LIST))); } + @Test + public void parse_shouldParseGlobalPropertyPrivileges() throws IOException { + // setup + Document config = buildOnValidConfigXml() + .withGlobalProperty("report.deleteReportsAgeInHours", "72", "some", + null, null, "Some Privilege For View Global Properties", + "Some Privilege For Edit Global Properties", "Some Privilege For Delete Global Properties") + .build(); + + // replay + Module module = parser.parse(writeConfigXmlToFile(config)); + + // verify + assertThat(module.getGlobalProperties().get(0).getViewPrivilege().getPrivilege(), is("Some Privilege For View Global Properties")); + assertThat(module.getGlobalProperties().get(0).getEditPrivilege().getPrivilege(), is("Some Privilege For Edit Global Properties")); + assertThat(module.getGlobalProperties().get(0).getDeletePrivilege().getPrivilege(), is("Some Privilege For Delete Global Properties")); + } + private void expectModuleExceptionWithMessage(Executable executable, String expectedMessage) { ModuleException exception = assertThrows(ModuleException.class, executable); assertThat(exception.getMessage(), startsWith(expectedMessage)); @@ -1175,12 +1302,10 @@ private void whenGettingMessageFromMessageSourceServiceWithKeyReturnSameKey(Stri } private ModuleConfigXmlBuilder buildOnValidConfigXml() { - return buildOnValidConfigXml("1.6"); } private ModuleConfigXmlBuilder buildOnValidConfigXml(String version) { - return new ModuleConfigXmlBuilder(documentBuilder) .withModuleRoot() .withConfigVersion(version) @@ -1266,6 +1391,17 @@ public ModuleConfigXmlBuilder withAwareOfModules(String... modules) { return this; } + public ModuleConfigXmlBuilder withStartBeforeModules(String... modules) { + Element startBeforeModules = configXml.createElement("start_before_modules"); + for (String module : modules) { + Element startBeforeModule = configXml.createElement("start_before_module"); + startBeforeModule.setTextContent(module); + startBeforeModules.appendChild(startBeforeModule); + } + configXml.getDocumentElement().appendChild(startBeforeModules); + return this; + } + public ModuleConfigXmlBuilder withPrivilege(String name, String description) { Map children = new HashMap<>(); if (name != null) { @@ -1300,7 +1436,7 @@ public ModuleConfigXmlBuilder withAdvice(String point, String className) { } public ModuleConfigXmlBuilder withGlobalProperty(String property, String defaultValue, String description, - String datatypeClassname, String datatypeConfig) { + String datatypeClassname, String datatypeConfig, String viewPrivilege, String editPrivilege, String deletePrivilege) { Map children = new HashMap<>(); if (property != null) { children.put("property", property); @@ -1317,6 +1453,15 @@ public ModuleConfigXmlBuilder withGlobalProperty(String property, String default if (datatypeConfig != null) { children.put("datatypeConfig", datatypeConfig); } + if (viewPrivilege != null) { + children.put("viewPrivilege", viewPrivilege); + } + if (editPrivilege != null) { + children.put("editPrivilege", editPrivilege); + } + if (deletePrivilege != null) { + children.put("deletePrivilege", deletePrivilege); + } return withElementsAttachedToRoot("globalProperty", children); } diff --git a/api/src/test/java/org/openmrs/module/dtd/ConfigXmlBuilder.java b/api/src/test/java/org/openmrs/module/dtd/ConfigXmlBuilder.java new file mode 100644 index 000000000000..5b74ba420a0e --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ConfigXmlBuilder.java @@ -0,0 +1,939 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * ConfigXmlBuilder is a utility class for tests that need to build a configuration XML file for OpenMRS. + * It provides a fluent API to add various elements to the configuration file. + * + * The structure of the XML is dictated by a Document Type Definition (DTD), + * the version of which is passed into the constructor of this class. + * + * Note: This class is intended for use in testing scenarios only. Do not use it for + * generating configuration XML files for production use. + */ +public class ConfigXmlBuilder { + + private static final String MODULE_NAME = "module"; + + private static final String TAG_ID = "id"; + + private static final String TAG_NAME = "name"; + + private static final String TAG_VERSION = "version"; + + private static final String TAG_PACKAGE = "package"; + + private static final String TAG_AUTHOR = "author"; + + private static final String TAG_DESCRIPTION = "description"; + + private static final String TAG_ACTIVATOR = "activator"; + + private static final String TAG_REQUIRE_MODULES = "require_modules"; + + private static final String TAG_REQUIRE_MODULE = "require_module"; + + private static final String TAG_UPDATE_URL = "updateURL"; + + private static final String TAG_REQUIRE_VERSION = "require_version"; + + private static final String TAG_REQUIRE_DATABASE_VERSION = "require_database_version"; + + private static final String TAG_LIBRARY = "library"; + + private static final String TAG_EXTENSION = "extension"; + + private static final String TAG_ADVICE = "advice"; + + private static final String TAG_PRIVILEGE = "privilege"; + + private static final String TAG_GLOBAL_PROPERTY = "globalProperty"; + + private static final String TAG_DWR = "dwr"; + + private static final String TAG_SERVLET = "servlet"; + + private static final String TAG_MESSAGES = "messages"; + + private static final String TAG_FILTER = "filter"; + + private static final String TAG_FILTER_MAPPING = "filter-mapping"; + + private static final String TAG_AWARE_OF_MODULES = "aware_of_modules"; + + private static final String TAG_AWARE_OF_MODULE = "aware_of_module"; + + private static final String TAG_CONDITIONAL_RESOURCES = "conditionalResources"; + + private static final String TAG_CONDITIONAL_RESOURCE = "conditionalResource"; + + private static final String TAG_MAPPING_FILES = "mappingFiles"; + + private static final String TAG_MANDATORY = "mandatory"; + + private static final String TAG_PACKAGED_WITH_MAPPED_CLASSES = "packagesWithMappedClasses"; + + private static final String TAG_POINT = "point"; + + private static final String TAG_CLASS = "class"; + + private static final String TAG_PROPERTY = "property"; + + private static final String TAG_DEFAULT_VALUE = "defaultValue"; + + private static final String TAG_ALLOW ="allow" ; + + private static final String TAG_SIGNATURES = "signatures"; + + private static final String TAG_CREATE = "create"; + + private static final String TAG_PARAM = "param"; + + private static final String TAG_INCLUDE = "include"; + + private static final String TAG_METHOD = "method"; + + private static final String TAG_CONVERT = "convert"; + + private static final String TAG_FILTER_NAME = "filter-name"; + + private static final String TAG_FILTER_CLASS = "filter-class"; + + private static final String TAG_INIT_PARAM = "init-param"; + + private static final String TAG_PARAM_NAME = "param-name"; + + private static final String TAG_URL_PATTERN = "url-pattern"; + + private static final String TAG_PARAM_VALUE = "param-value"; + + private static final String TAG_SERVLET_NAME = "servlet-name"; + + private static final String TAG_PATH = "path"; + + private static final String TAG_OPENMRS_VERSION = "openmrsVersion"; + + private static final String TAG_LOAD_MODULES_IF_PRESENT = "loadIfModulesPresent"; + + private static final String TAG_OPENMRS_MODULE = "openmrsModule"; + + private static final String TAG_MODULE_ID = "moduleId"; + + private static final String ATTRIBUTE_ID = "id"; + + private static final String ATTRIBUTE_PATH = "path"; + + private static final String ATTRIBUTE_TYPE = "type"; + + private static final String ATTRIBUTE_CREATOR = "creator"; + + private static final String ATTRIBUTE_JAVASCRIPT = "javascript"; + + private static final String ATTRIBUTE_NAME = "name"; + + private static final String ATTRIBUTE_VALUE = "value"; + + private static final String ATTRIBUTE_CONVERTER = "converter"; + + private static final String ATTRIBUTE_MATCH = "match"; + + private static final String ATTRIBUTE_VERSION = "version"; + + private static final String PUBLIC_IDENTIFIER = "-//OpenMRS//DTD OpenMRS Config 1.0//EN"; + + private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + + private final String dtdVersion; + + private Document configXml; + + /** + * Constructs a new ConfigXmlBuilder for testing purposes. + * + * @param dtdVersion the version of the DTD (Document Type Definition) to use + * for validating the generated XML. The DTD dictates the structure + * and permissible values of the XML document. + * + * @throws ParserConfigurationException if a DocumentBuilder cannot be created which + * satisfies the configuration requested. + * + * Note: This constructor is intended for use in testing scenarios only. Do not use it for + * generating configuration XML files for production use. + */ + public ConfigXmlBuilder(String dtdVersion) throws ParserConfigurationException, FileNotFoundException, URISyntaxException { + this.dtdVersion = dtdVersion; + initDocument(); + setDocType(); + } + + /** + * Converts the provided XML Document to an InputStream. + * + * This can be useful for tests or other scenarios where you need to consume + * the XML document as a stream (e.g., when passing the document to a method + * that requires an InputStream). + * + * Note: This method does not close the provided Document or the resulting InputStream. + * It's the caller's responsibility to close the stream after use. + * + * @param document the XML Document to convert to an InputStream + * @return an InputStream representing the provided Document + * + * @throws TransformerException if an unrecoverable error occurs during the transformation + */ + public static InputStream writeToInputStream(Document document) throws TransformerException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Source xmlSource = new DOMSource(document); + Result outputTarget = new StreamResult(outputStream); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, document.getDoctype().getPublicId()); + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, document.getDoctype().getSystemId()); + transformer.transform(xmlSource, outputTarget); + return new ByteArrayInputStream(outputStream.toByteArray()); + } + + protected static ConfigXmlBuilder withMinimalTags(String dtdVersion) throws ParserConfigurationException, FileNotFoundException, URISyntaxException { + return new ConfigXmlBuilder(dtdVersion).withId("basicexample").withName("Basicexample").withVersion("1.2.3") + .withPackage("org.openmrs.module.basicexample").withAuthor("Community").withDescription("First module") + .withActivator("org.openmrs.module.basicexample.BasicexampleActivator"); + } + + private void initDocument() throws ParserConfigurationException { + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + configXml = documentBuilder.newDocument(); + documentBuilder.newDocument(); + } + + private void setDocType() throws FileNotFoundException, URISyntaxException { + DOMImplementation domImpl = configXml.getImplementation(); + + URL dtdResource = ConfigXmlBuilder.class.getResource("/org/openmrs/module/dtd/config-" + dtdVersion + ".dtd"); + if (dtdResource == null) { + throw new FileNotFoundException("DTD file not found for version " + dtdVersion); + } + String dtdUri = dtdResource.toURI().toString(); + DocumentType doctype = domImpl.createDocumentType(MODULE_NAME, PUBLIC_IDENTIFIER, dtdUri); + configXml.appendChild(doctype); + Element root = configXml.createElement(MODULE_NAME); + configXml.appendChild(root); + } + + public ConfigXmlBuilder withId(String id) { + createElementWithText(TAG_ID, id); + return this; + } + + public ConfigXmlBuilder withName(String name) { + createElementWithText(TAG_NAME, name); + return this; + } + + public ConfigXmlBuilder withVersion(String version) { + createElementWithText(TAG_VERSION, version); + return this; + } + + public ConfigXmlBuilder withPackage(String packageName) { + createElementWithText(TAG_PACKAGE, packageName); + return this; + } + + public ConfigXmlBuilder withAuthor(String author) { + createElementWithText(TAG_AUTHOR, author); + return this; + } + + public ConfigXmlBuilder withDescription(String description) { + createElementWithText(TAG_DESCRIPTION, description); + return this; + } + + public ConfigXmlBuilder withActivator(String activator) { + createElementWithText(TAG_ACTIVATOR, activator); + return this; + } + + public ConfigXmlBuilder withInvalidTag(String text) { + createElementWithText("invalid_tag", text); + return this; + } + + public ConfigXmlBuilder withUpdateUrl(String updateUrl) { + createElementWithText(TAG_UPDATE_URL, updateUrl); + return this; + } + + public ConfigXmlBuilder withRequireVersion(String requireVersion) { + createElementWithText(TAG_REQUIRE_VERSION, requireVersion); + return this; + } + + public ConfigXmlBuilder withRequireDatabaseVersion(String requireDatabaseVersion) { + createElementWithText(TAG_REQUIRE_DATABASE_VERSION, requireDatabaseVersion); + return this; + } + + public ConfigXmlBuilder withMappingFiles(String mappingFiles) { + createElementWithText(TAG_MAPPING_FILES, mappingFiles); + return this; + } + + public ConfigXmlBuilder withMandatory(String mandatory) { + createElementWithText(TAG_MANDATORY, mandatory); + return this; + } + + public ConfigXmlBuilder withPackagesWithMappedClasses(String packagesWithMappedClasses) { + createElementWithText(TAG_PACKAGED_WITH_MAPPED_CLASSES, packagesWithMappedClasses); + return this; + } + + public ConfigXmlBuilder withLibrary(Optional id, Optional path, Optional type) { + Element element = configXml.createElement(TAG_LIBRARY); + id.ifPresent(libraryId -> element.setAttribute(ATTRIBUTE_ID, libraryId)); + path.ifPresent(libraryPath -> element.setAttribute(ATTRIBUTE_PATH, libraryPath)); + type.ifPresent(libraryType -> element.setAttribute(ATTRIBUTE_TYPE, libraryType)); + configXml.getDocumentElement().appendChild(element); + return this; + } + + public ConfigXmlBuilder withExtension(Optional point, Optional cls) { + Element extensionElement = configXml.createElement(TAG_EXTENSION); + point.ifPresent(extensionPoint -> { + Element pointElement = configXml.createElement(TAG_POINT); + pointElement.setTextContent(point.get()); + extensionElement.appendChild(pointElement); + }); + cls.ifPresent(extensionClass -> { + Element classElement = configXml.createElement(TAG_CLASS); + classElement.setTextContent(cls.get()); + extensionElement.appendChild(classElement); + }); + configXml.getDocumentElement().appendChild(extensionElement); + return this; + } + + public ConfigXmlBuilder withAdvice(Optional point, Optional cls) { + Element adviceElement = configXml.createElement(TAG_ADVICE); + point.ifPresent(advicePoint -> { + Element pointElement = configXml.createElement(TAG_POINT); + pointElement.setTextContent(point.get()); + adviceElement.appendChild(pointElement); + }); + cls.ifPresent(adviceClass -> { + Element classElement = configXml.createElement(TAG_CLASS); + classElement.setTextContent(cls.get()); + adviceElement.appendChild(classElement); + }); + configXml.getDocumentElement().appendChild(adviceElement); + return this; + } + + public ConfigXmlBuilder withPrivilege(Optional name, Optional description) { + Element privilegeElement = configXml.createElement(TAG_PRIVILEGE); + name.ifPresent(privilegeName -> { + Element nameElement = configXml.createElement(TAG_NAME); + nameElement.setTextContent(name.get()); + privilegeElement.appendChild(nameElement); + }); + description.ifPresent(privilegeDescription -> { + Element descriptionElement = configXml.createElement(TAG_DESCRIPTION); + descriptionElement.setTextContent(description.get()); + privilegeElement.appendChild(descriptionElement); + }); + configXml.getDocumentElement().appendChild(privilegeElement); + return this; + } + + public ConfigXmlBuilder withGlobalProperty(Optional property, Optional defaultValue, + Optional description) { + Element globalPropertyElement = configXml.createElement(TAG_GLOBAL_PROPERTY); + property.ifPresent(propertyValue -> { + Element propertyElement = configXml.createElement(TAG_PROPERTY); + propertyElement.setTextContent(property.get()); + globalPropertyElement.appendChild(propertyElement); + }); + defaultValue.ifPresent(privilegeDefaultValue -> { + Element defaultElement = configXml.createElement(TAG_DEFAULT_VALUE); + defaultElement.setTextContent(defaultValue.get()); + globalPropertyElement.appendChild(defaultElement); + }); + description.ifPresent(privilegeDescription -> { + Element descriptionElement = configXml.createElement(TAG_DESCRIPTION); + descriptionElement.setTextContent(description.get()); + globalPropertyElement.appendChild(descriptionElement); + }); + configXml.getDocumentElement().appendChild(globalPropertyElement); + return this; + } + + public ConfigXmlBuilder withDwr(Dwr dwr) { + Element dwrElement = configXml.createElement(TAG_DWR); + + dwr.allow.ifPresent(allow -> { + Element allowElement = configXml.createElement(TAG_ALLOW); + dwrElement.appendChild(allowElement); + withCreates(allowElement, allow.getCreates()); + withConverts(allowElement, allow.getConverts()); + }); + + dwr.getSignatures().ifPresent(dwrSig -> createElementWithText(dwrElement, TAG_SIGNATURES, dwrSig)); + + configXml.getDocumentElement().appendChild(dwrElement); + return this; + } + + public void withCreates(Element parent, List creates) { + for (Create create : creates) { + Element createElement = configXml.createElement(TAG_CREATE); + + create.getAttCreator().ifPresent(creatorVal -> createElement.setAttribute(ATTRIBUTE_CREATOR, creatorVal)); + create.getAttJavascript().ifPresent(javascriptVal -> createElement.setAttribute(ATTRIBUTE_JAVASCRIPT, javascriptVal)); + + create.getParam().ifPresent(createParam -> { + Element paramElement = configXml.createElement(TAG_PARAM); + createParam.getAttName().ifPresent(attNameVal -> paramElement.setAttribute(ATTRIBUTE_NAME, attNameVal)); + createParam.getAttValue().ifPresent(attValueVal -> paramElement.setAttribute(ATTRIBUTE_VALUE, attValueVal)); + createElement.appendChild(paramElement); + }); + + for (Include include : create.getIncludes()) { + Element includeElement = configXml.createElement(TAG_INCLUDE); + include.method.ifPresent(method -> includeElement.setAttribute(TAG_METHOD, method)); + createElement.appendChild(includeElement); + } + + parent.appendChild(createElement); + } + } + + public void withConverts(Element parent, List converts) { + for (Convert convert : converts) { + + Element convertElement = configXml.createElement(TAG_CONVERT); + + convert.getParam().ifPresent(createParam -> { + Element paramElement = configXml.createElement(TAG_PARAM); + createParam.getAttName().ifPresent(attNameVal -> paramElement.setAttribute(ATTRIBUTE_NAME, attNameVal)); + createParam.getAttValue().ifPresent(attValueVal -> paramElement.setAttribute(ATTRIBUTE_VALUE, attValueVal)); + convertElement.appendChild(paramElement); + }); + + convert.getConverter().ifPresent(convertVal -> convertElement.setAttribute(ATTRIBUTE_CONVERTER, convertVal)); + convert.getMatch().ifPresent(matchVal -> convertElement.setAttribute(ATTRIBUTE_MATCH, matchVal)); + + parent.appendChild(convertElement); + } + } + + public ConfigXmlBuilder withRequireModules(String... modules) { + Element requireModulesElement = configXml.createElement(TAG_REQUIRE_MODULES); + configXml.getDocumentElement().appendChild(requireModulesElement); + + for (String module : modules) { + createElementWithText(requireModulesElement, TAG_REQUIRE_MODULE, module); + } + + return this; + } + + public ConfigXmlBuilder withRequireModules(List modules, List> versions) { + assert modules.size() == versions.size(); + Element requireModulesElement = configXml.createElement(TAG_REQUIRE_MODULES); + configXml.getDocumentElement().appendChild(requireModulesElement); + + for (int i = 0; i < modules.size(); i++) { + String module = modules.get(i); + + Element requireModuleElement = configXml.createElement(TAG_REQUIRE_MODULE); + requireModuleElement.setTextContent(module); + + createElementWithText(requireModulesElement, TAG_REQUIRE_MODULE, module); + versions.get(i).ifPresent(version -> requireModuleElement.setAttribute(ATTRIBUTE_VERSION, version)); + } + + return this; + } + + public ConfigXmlBuilder withServlet(Optional servletName, Optional servletClass, Map initParams) { + Element servletElement = configXml.createElement(TAG_SERVLET); + + servletName.ifPresent(servletNameVal -> { + Element servletNameElement = configXml.createElement("servlet-name"); + servletNameElement.setTextContent(servletNameVal); + servletElement.appendChild(servletNameElement); + }); + + servletClass.ifPresent(servletClassVal -> { + Element servletClassElement = configXml.createElement("servlet-class"); + servletClassElement.setTextContent(servletClassVal); + servletElement.appendChild(servletClassElement); + }); + + for (Map.Entry entry : initParams.entrySet()) { + Element initParamElement = configXml.createElement("init-param"); + + Element paramNameElement = configXml.createElement("param-name"); + paramNameElement.setTextContent(entry.getKey()); + initParamElement.appendChild(paramNameElement); + + Element paramValueElement = configXml.createElement("param-value"); + paramValueElement.setTextContent(entry.getValue()); + initParamElement.appendChild(paramValueElement); + + servletElement.appendChild(initParamElement); + } + + configXml.getDocumentElement().appendChild(servletElement); + return this; + } + + public ConfigXmlBuilder withMessages(Optional lang, Optional file) { + Element messagesElement = configXml.createElement(TAG_MESSAGES); + lang.ifPresent(langVal -> { + Element langElement = configXml.createElement("lang"); + langElement.setTextContent(langVal); + messagesElement.appendChild(langElement); + }); + file.ifPresent(fileVal -> { + Element fileElement = configXml.createElement("file"); + fileElement.setTextContent(fileVal); + messagesElement.appendChild(fileElement); + }); + configXml.getDocumentElement().appendChild(messagesElement); + return this; + } + + public ConfigXmlBuilder withFilter(Filter filter) { + Element filterElement = configXml.createElement(TAG_FILTER); + + filter.filterName.ifPresent(filterName -> { + Element filterNameElement = configXml.createElement(TAG_FILTER_NAME); + filterNameElement.setTextContent(filterName); + filterElement.appendChild(filterNameElement); + }); + filter.filterClass.ifPresent(filterClass -> { + Element filterClassElement = configXml.createElement(TAG_FILTER_CLASS); + filterClassElement.setTextContent(filterClass); + filterElement.appendChild(filterClassElement); + }); + + for (InitParam initParam : filter.getInitParams()) { + Element initParamElement = configXml.createElement(TAG_INIT_PARAM); + + filterElement.appendChild(initParamElement); + initParam.getParamName().ifPresent(initParamName -> { + createElementWithText(initParamElement, TAG_PARAM_NAME, initParamName); + }); + initParam.getParamValue().ifPresent(initParamValue -> { + createElementWithText(initParamElement, TAG_PARAM_VALUE, initParamValue); + }); + } + + configXml.getDocumentElement().appendChild(filterElement); + return this; + } + + public ConfigXmlBuilder withFilterMapping(FilterMapping filterMapping) { + Element filterElement = configXml.createElement(TAG_FILTER_MAPPING); + + filterMapping.filterName.ifPresent(filterName -> { + Element filterNameElement = configXml.createElement(TAG_FILTER_NAME); + filterNameElement.setTextContent(filterName); + filterElement.appendChild(filterNameElement); + }); + filterMapping.urlPattern.ifPresent(urlPattern -> { + Element urlPatternElement = configXml.createElement(TAG_URL_PATTERN); + urlPatternElement.setTextContent(urlPattern); + filterElement.appendChild(urlPatternElement); + }); + filterMapping.servletName.ifPresent(servletName -> { + Element servletNameElement = configXml.createElement(TAG_SERVLET_NAME); + servletNameElement.setTextContent(servletName); + filterElement.appendChild(servletNameElement); + }); + + configXml.getDocumentElement().appendChild(filterElement); + return this; + } + + public ConfigXmlBuilder withAwareOfModules(List awareOfModules) { + Element awareOfModulesElement = configXml.createElement(TAG_AWARE_OF_MODULES); + + for (AwareOfModule awareOfModule : awareOfModules) { + + Element awareOfModuleElement = configXml.createElement(TAG_AWARE_OF_MODULE); + + awareOfModule.getAwareOfModule().ifPresent(awareOfModuleValue -> { + awareOfModuleElement.setTextContent(awareOfModuleValue); + awareOfModule.getVersionAtt().ifPresent(versionAtt -> awareOfModuleElement.setAttribute(ATTRIBUTE_VERSION, versionAtt)); + }); + awareOfModulesElement.appendChild(awareOfModuleElement); + } + + configXml.getDocumentElement().appendChild(awareOfModulesElement); + return this; + } + + public ConfigXmlBuilder withConditionalResources(List conditionalResources) { + Element conditionalResourcesElement = configXml.createElement(TAG_CONDITIONAL_RESOURCES); + + for (ConditionalResource conditionalResource : conditionalResources) { + + Element conditionalResourceElement = configXml.createElement(TAG_CONDITIONAL_RESOURCE); + + conditionalResource.getPath().ifPresent(path -> createElementWithText(conditionalResourceElement, TAG_PATH, path)); + conditionalResource.getOpenmrsVersion().ifPresent(openmrsVersion -> createElementWithText(conditionalResourceElement, TAG_OPENMRS_VERSION, openmrsVersion)); + + if (conditionalResource.getLoadIfModulesPresent().size() > 0) { + Element loadIfModulesPresentElement = configXml.createElement(TAG_LOAD_MODULES_IF_PRESENT); + conditionalResourceElement.appendChild(loadIfModulesPresentElement); + + for (OpenMRSModule openMRSModule : conditionalResource.getLoadIfModulesPresent()) { + + Element openMRSModuleElement = configXml.createElement(TAG_OPENMRS_MODULE); + + openMRSModule.getModuleId().ifPresent(moduleId -> createElementWithText(openMRSModuleElement, TAG_MODULE_ID, moduleId)); + openMRSModule.getVersion().ifPresent(version -> createElementWithText(openMRSModuleElement, TAG_VERSION, version)); + + loadIfModulesPresentElement.appendChild(openMRSModuleElement); + } + } + + conditionalResourcesElement.appendChild(conditionalResourceElement); + } + + configXml.getDocumentElement().appendChild(conditionalResourcesElement); + return this; + } + + private void createElementWithText(String tag, String text) { + createElementWithText(configXml.getDocumentElement(), tag, text); + } + + private void createElementWithText(Node parent, String tag, String text) { + Element element = configXml.createElement(tag); + element.setTextContent(text); + parent.appendChild(element); + } + + public Document build() throws TransformerException { + return configXml; + } + + protected static final class Dwr { + + private final Optional allow; + + private final Optional signatures; + + public Dwr(Optional allow, Optional signatures) { + this.allow = allow; + this.signatures = signatures; + } + + public Optional getSignatures() { + return signatures; + } + + public Optional getAllow() { + return allow; + } + } + + protected static final class Allow { + + private final List creates = new ArrayList<>(); + + private final List converts = new ArrayList<>(); + + public void addCreate(Create create) { + this.creates.add(create); + } + + public void addConvert(Convert convert) { + this.converts.add(convert); + } + + public List getCreates() { + return new ArrayList<>(creates); + } + + public List getConverts() { + return new ArrayList<>(converts); + } + } + + protected static final class Create { + + private final Optional param; + + private final Optional attCreator; + + private final Optional attJavascript; + + private final List includes = new ArrayList<>(); + + public Create(Optional attCreator, Optional attJavascript, Optional param) { + this.param = param; + this.attCreator = attCreator; + this.attJavascript = attJavascript; + } + + public void addIncludes(Include include) { + this.includes.add(include); + } + + public Optional getParam() { + return param; + } + + public Optional getAttCreator() { + return attCreator; + } + + public Optional getAttJavascript() { + return attJavascript; + } + + public List getIncludes() { + return new ArrayList<>(includes); + } + } + + protected static final class Convert { + + private final Optional param; + + private final Optional converter; + + private final Optional match; + + public Convert(Optional param, Optional converter, Optional match) { + this.param = param; + this.converter = converter; + this.match = match; + } + + public Optional getParam() { + return param; + } + + public Optional getConverter() { + return converter; + } + + public Optional getMatch() { + return match; + } + } + + protected static final class Param { + + private final Optional attName; + + private final Optional attValue; + + public Param(Optional attName, Optional attValue) { + this.attName = attName; + this.attValue = attValue; + } + + public Optional getAttName() { + return attName; + } + + public Optional getAttValue() { + return attValue; + } + } + + protected static final class Include { + + private final Optional method; + + public Include(Optional method) { + this.method = method; + } + + public Optional getMethod() { + return method; + } + } + + protected static final class Filter { + + private final Optional filterName; + private final Optional filterClass; + private final List initParams = new ArrayList<>(); + + public Filter(Optional filterName, Optional filterClass) { + this.filterName = filterName; + this.filterClass = filterClass; + } + + public Optional getFilterName() { + return filterName; + } + + public Optional getFilterClass() { + return filterClass; + } + + public void addInitParam(InitParam initParam) { + this.initParams.add(initParam); + } + + public List getInitParams() { + return new ArrayList<>(initParams); + } + } + + protected static final class InitParam { + + private final Optional paramName; + private final Optional paramValue; + + public InitParam(Optional paramName, Optional paramValue) { + this.paramName = paramName; + this.paramValue = paramValue; + } + + public Optional getParamName() { + return paramName; + } + + public Optional getParamValue() { + return paramValue; + } + } + + protected static final class FilterMapping { + + private final Optional filterName; + private final Optional urlPattern; + private final Optional servletName; + + public FilterMapping(Optional filterName, Optional urlPattern, Optional servletName) { + this.filterName = filterName; + this.urlPattern = urlPattern; + this.servletName = servletName; + } + + public Optional getFilterName() { + return filterName; + } + + public Optional getUrlPattern() { + return urlPattern; + } + + public Optional getServletName() { + return servletName; + } + } + + protected static final class AwareOfModule { + + private final Optional awareOfModule; + private final Optional versionAtt; + + public AwareOfModule(Optional awareOfModule, Optional versionAtt) { + this.awareOfModule = awareOfModule; + this.versionAtt = versionAtt; + } + + public Optional getAwareOfModule() { + return awareOfModule; + } + + public Optional getVersionAtt() { + return versionAtt; + } + } + + protected static final class ConditionalResource { + + private final Optional path; + private final Optional openmrsVersion; + private final List loadIfModulesPresent = new ArrayList<>(); + + public ConditionalResource(Optional path, Optional openmrsVersion) { + this.path = path; + this.openmrsVersion = openmrsVersion; + } + + public Optional getPath() { + return path; + } + + public Optional getOpenmrsVersion() { + return openmrsVersion; + } + + public void addModule(OpenMRSModule module) { + loadIfModulesPresent.add(module); + } + + public List getLoadIfModulesPresent() { + return new ArrayList<>(loadIfModulesPresent); + } + } + + protected static final class OpenMRSModule { + + private final Optional moduleId; + private final Optional version; + + public OpenMRSModule(Optional moduleId, Optional version) { + this.moduleId = moduleId; + this.version = version; + } + + public Optional getModuleId() { + return moduleId; + } + + public Optional getVersion() { + return version; + } + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/DtdTestValidator.java b/api/src/test/java/org/openmrs/module/dtd/DtdTestValidator.java new file mode 100644 index 000000000000..2608d8334667 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/DtdTestValidator.java @@ -0,0 +1,68 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; + +public class DtdTestValidator { + + private static final Logger log = LoggerFactory.getLogger(DtdTestValidator.class); + + private DtdTestValidator() { + } + + public static boolean isValidConfigXml(InputStream xml) { + try { + DocumentBuilderFactory domFactory; + DocumentBuilder builder; + domFactory = DocumentBuilderFactory.newInstance(); + domFactory.setValidating(true); + builder = domFactory.newDocumentBuilder(); + final boolean[] isValidConfig = { true }; + + builder.setErrorHandler(new ErrorHandler() { + + @Override + public void warning(SAXParseException e) { + isValidConfig[0] = true; + + } + + @Override + public void error(SAXParseException e) { + e.printStackTrace(); + isValidConfig[0] = false; + } + + @Override + public void fatalError(SAXParseException e) { + e.printStackTrace(); + isValidConfig[0] = false; + } + }); + builder.parse(xml); + return isValidConfig[0]; + } + catch (SAXException | IOException | ParserConfigurationException e) { + log.error("Failure reason: ", e); + return false; + } + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_0.java b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_0.java new file mode 100644 index 000000000000..2311b370e4c4 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_0.java @@ -0,0 +1,890 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmrs.module.dtd.ConfigXmlBuilder.withMinimalTags; +import static org.openmrs.module.dtd.ConfigXmlBuilder.writeToInputStream; +import static org.openmrs.module.dtd.DtdTestValidator.isValidConfigXml; + +public class ModuleConfigDTDTest_V1_0 { + + private static final String[] compatibleVersions = new String[] { "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7" }; + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlWithMinimalRequirements(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWhenOutOfOrder(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withName("Basicexample") // id should be first + .withId("basicexample").withVersion("1.2.3").withPackage("org.openmrs.module.basicexample") + .withAuthor("Community").withDescription("First module") + .withActivator("org.openmrs.module.basicexample.BasicexampleActivator") + .withInvalidTag("org.openmrs.module.basicexample.BasicexampleActivator").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWithMissingId(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withName("Basicexample").withVersion("1.2.3") + .withPackage("org.openmrs.module.basicexample").withAuthor("Community").withDescription("First module") + .withActivator("org.openmrs.module.basicexample.BasicexampleActivator").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWithMissingName(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withVersion("1.2.3") + .withPackage("org.openmrs.module.basicexample").withAuthor("Community").withDescription("First module") + .withActivator("org.openmrs.module.basicexample.BasicexampleActivator").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWithMissingVersion(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withName("Basicexample") + .withPackage("org.openmrs.module.basicexample").withAuthor("Community").withDescription("First module") + .withActivator("org.openmrs.module.basicexample.BasicexampleActivator").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWithMissingPackage(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withName("Basicexample") + .withVersion("1.2.3").withAuthor("Community").withDescription("First module") + .withActivator("org.openmrs.module.basicexample.BasicexampleActivator").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWithMissingAuthor(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withName("Basicexample") + .withVersion("1.2.3").withPackage("org.openmrs.module.basicexample").withDescription("First module") + .withActivator("org.openmrs.module.basicexample.BasicexampleActivator").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWithMissingDescription(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withName("Basicexample") + .withVersion("1.2.3").withPackage("org.openmrs.module.basicexample").withAuthor("Community") + .withActivator("org.openmrs.module.basicexample.BasicexampleActivator").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWithMissingActivator(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withName("Basicexample") + .withVersion("1.2.3").withPackage("org.openmrs.module.basicexample").withAuthor("Community") + .withDescription("First module").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidRequireModulesWithSingleModule(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withName("Basicexample") + .withVersion("1.2.3").withPackage("org.openmrs.module.basicexample").withAuthor("Community") + .withDescription("First module").withActivator("org.openmrs.module.basicexample.BasicexampleActivator") + .withRequireModules("module1").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidRequireModulesWithMultipleModules(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withName("Basicexample") + .withVersion("1.2.3").withPackage("org.openmrs.module.basicexample").withAuthor("Community") + .withDescription("First module").withActivator("org.openmrs.module.basicexample.BasicexampleActivator") + .withRequireModules("module1", "module2", "module3").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlequireModulesMissingModules(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = new ConfigXmlBuilder(version).withId("basicexample").withName("Basicexample") + .withVersion("1.2.3").withPackage("org.openmrs.module.basicexample").withAuthor("Community") + .withDescription("First module").withActivator("org.openmrs.module.basicexample.BasicexampleActivator") + .withRequireModules().build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlFailsWithInvalidTag(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withInvalidTag("some text").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlLibraryWithResourcesType(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withLibrary(Optional.of("id"), Optional.of("path/to/library"), Optional.of("resources")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlLibraryWithLibraryType(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withLibrary(Optional.of("id"), Optional.of("path/to/library"), Optional.of("library")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlMultipleLibraries(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withLibrary(Optional.of("id1"), Optional.of("path/to/library1"), Optional.of("resources")) + .withLibrary(Optional.of("id2"), Optional.of("path/to/library2"), Optional.of("library")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlLibraryWithMissingId(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withLibrary(Optional.empty(), Optional.of("path/to/library"), Optional.of("library")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlLibraryWithMissingPath(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withLibrary(Optional.of("id"), Optional.empty(), Optional.of("library")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlLibraryWithMissingLibrary(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withLibrary(Optional.of("id"), Optional.of("path/to/library"), Optional.empty()).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlLibraryWithInvalidLibrary(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withLibrary(Optional.of("id"), Optional.of("path/to/library"), Optional.of("invalid")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidExtension(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withExtension(Optional.of("org.openmrs.extensionPoint"), Optional.of("ExampleExtension")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlExtensionMissingPoint(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withExtension(Optional.empty(), Optional.of("ExampleExtension")) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlExtensionMissingClass(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withExtension(Optional.of("org.openmrs.extensionPoint"), Optional.empty()).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidAdvice(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withAdvice(Optional.of("org.openmrs.advicePoint"), Optional.of("ExampleAdvice")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlAdviceMissingPoint(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withExtension(Optional.empty(), Optional.of("ExampleAdvice")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlAdviceMissingClass(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = null; + try { + configXml = withMinimalTags(version).withExtension(Optional.of("org.openmrs.advicePoint"), Optional.empty()) + .build(); + } catch (TransformerException e) { + throw new RuntimeException(e); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidPrivilege(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = null; + try { + configXml = withMinimalTags(version).withPrivilege(Optional.of("Manage Reports"), Optional.of("Add report")) + .build(); + } catch (TransformerException e) { + throw new RuntimeException(e); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlPrivilegeMissingName(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withPrivilege(Optional.empty(), Optional.of("Add report")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlPrivilegeMissingDescription(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withPrivilege(Optional.of("Manage Reports"), Optional.empty()).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidGlobalProperty(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withGlobalProperty(Optional.of("report.deleteReportsAgeInHours"), + Optional.of("48"), Optional.of("delete reports after hours")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidGlobalPropertyWithoutDefault(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withGlobalProperty(Optional.of("report.deleteReportsAgeInHours"), + Optional.empty(), Optional.of("delete reports after hours")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlInvalidGlobalPropertyMissingProperty(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withGlobalProperty(Optional.empty(), Optional.empty(), Optional.of("delete reports after hours")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidGlobalPropertyMissingDescription(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withGlobalProperty(Optional.of("report.deleteReportsAgeInHours"), Optional.empty(), Optional.empty()) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidDwrAllFields(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.of("javascript1"), + Optional.of(param1)); + ConfigXmlBuilder.Create create2 = new ConfigXmlBuilder.Create(Optional.of("creator2"), Optional.of("javascript2"), + Optional.of(param2)); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.of(param1), Optional.of("converter1"), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.of("match2")); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + allow.addCreate(create2); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.of("signatures")); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidDwrWithoutSigs(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.of("javascript1"), + Optional.of(param1)); + ConfigXmlBuilder.Create create2 = new ConfigXmlBuilder.Create(Optional.of("creator2"), Optional.of("javascript2"), + Optional.of(param2)); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.of(param1), Optional.of("converter1"), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.of("match2")); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + allow.addCreate(create2); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.empty()); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidDwrWithoutCreate(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.of(param1), Optional.of("converter1"), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.of("match2")); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.empty()); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidDwrWithoutConvert(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.of("javascript1"), + Optional.of(param1)); + ConfigXmlBuilder.Create create2 = new ConfigXmlBuilder.Create(Optional.of("creator2"), Optional.of("javascript2"), + Optional.of(param2)); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + allow.addCreate(create2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.of("signatures")); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlDwrFailsMissingAllow(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.empty(), Optional.empty()); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlDwrMissingCreateCreator(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.of("javascript1"), + Optional.of(param1)); + ConfigXmlBuilder.Create create2 = new ConfigXmlBuilder.Create(Optional.empty(), Optional.of("javascript2"), + Optional.of(param2)); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.of(param1), Optional.of("converter1"), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.of("match2")); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + allow.addCreate(create2); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.of("signatures")); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlDwrMissingJavascript(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.empty(), + Optional.of(param1)); + ConfigXmlBuilder.Create create2 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.of("javascript2"), + Optional.of(param1)); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.of(param1), Optional.of("converter1"), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.of("match2")); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + allow.addCreate(create2); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.of("signatures")); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlDwrMissingCreateParam(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.empty(), + Optional.empty()); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.of(param1), Optional.of("converter1"), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.of("match2")); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.of("signatures")); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlDwrCreateWithoutParam(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.of("javascript"), + Optional.empty()); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.empty(), Optional.of("converter1"), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.of("match2")); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.of("signatures")); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidDwrConvertMissingConverter(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.of("javascript1"), + Optional.of(param1)); + ConfigXmlBuilder.Create create2 = new ConfigXmlBuilder.Create(Optional.of("creator2"), Optional.of("javascript2"), + Optional.of(param2)); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.of(param1), Optional.empty(), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.of("match2")); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + allow.addCreate(create2); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.of("signatures")); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidDwrConverterMissingMatch(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.Param param1 = new ConfigXmlBuilder.Param(Optional.of("name1"), Optional.of("val1")); + ConfigXmlBuilder.Param param2 = new ConfigXmlBuilder.Param(Optional.of("name2"), Optional.of("val2")); + + ConfigXmlBuilder.Create create1 = new ConfigXmlBuilder.Create(Optional.of("creator1"), Optional.of("javascript1"), + Optional.of(param1)); + ConfigXmlBuilder.Create create2 = new ConfigXmlBuilder.Create(Optional.of("creator2"), Optional.of("javascript2"), + Optional.of(param2)); + + ConfigXmlBuilder.Convert convert1 = new ConfigXmlBuilder.Convert(Optional.of(param1), Optional.of("converter1"), + Optional.of("match1")); + ConfigXmlBuilder.Convert convert2 = new ConfigXmlBuilder.Convert(Optional.of(param2), Optional.of("converter2"), + Optional.empty()); + + ConfigXmlBuilder.Allow allow = new ConfigXmlBuilder.Allow(); + + allow.addCreate(create1); + allow.addCreate(create2); + + allow.addConvert(convert1); + allow.addConvert(convert2); + + ConfigXmlBuilder.Dwr dwr = new ConfigXmlBuilder.Dwr(Optional.of(allow), Optional.of("signatures")); + + Document configXml = withMinimalTags(version).withDwr(dwr).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidServlet(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withServlet(Optional.of("ServletName"), Optional.of("ServletClass"), Collections.emptyMap()).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlServletMissingName(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withServlet(Optional.empty(), Optional.of("ServletClass"), Collections.emptyMap()).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlServletMissingClass(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withServlet(Optional.of("ServletName"), Optional.empty(), Collections.emptyMap()).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlValidMessages(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withMessages(Optional.of("en-US"), Optional.of("file")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlMessagesMissingLang(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withMessages(Optional.empty(), Optional.of("file")).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmMissingFile(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withMessages(Optional.of("en-US"), Optional.empty()).build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlWithOptionalFields(String version) + throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version).withUpdateUrl("updateUrl").withRequireVersion("1.2.3") + .withRequireDatabaseVersion("1.2.4").withMappingFiles("mapping files").build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + private static Stream getCompatibleVersions() { + return Arrays.stream(compatibleVersions).map(Arguments::of); + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_1.java b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_1.java new file mode 100644 index 000000000000..dea2217a1dcc --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_1.java @@ -0,0 +1,83 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmrs.module.dtd.ConfigXmlBuilder.withMinimalTags; +import static org.openmrs.module.dtd.ConfigXmlBuilder.writeToInputStream; +import static org.openmrs.module.dtd.DtdTestValidator.isValidConfigXml; + +public class ModuleConfigDTDTest_V1_1 { + + private static final String[] compatibleVersions = new String[] { "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7" }; + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void requireModulesWithVersionsAttributeSet(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + List modules = new ArrayList<>(); + modules.add("module1"); + modules.add("module2"); + + List> versions = new ArrayList<>(); + versions.add(Optional.of("1.2.3")); + versions.add(Optional.of("1.2.4")); + + Document configXml = withMinimalTags(version) + .withRequireModules(modules, versions) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void requireModulesWithVersionsAttributeNotSet(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + + List modules = new ArrayList<>(); + modules.add("module1"); + modules.add("module2"); + + List> versions = new ArrayList<>(); + versions.add(Optional.empty()); + versions.add(Optional.empty()); + + Document configXml = withMinimalTags(version) + .withRequireModules(modules, versions) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + private static Stream getCompatibleVersions() { + return Arrays.stream(compatibleVersions).map(Arguments::of); + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_2.java b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_2.java new file mode 100644 index 000000000000..f5960eca25d8 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_2.java @@ -0,0 +1,194 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmrs.module.dtd.ConfigXmlBuilder.withMinimalTags; +import static org.openmrs.module.dtd.ConfigXmlBuilder.writeToInputStream; +import static org.openmrs.module.dtd.DtdTestValidator.isValidConfigXml; + +public class ModuleConfigDTDTest_V1_2 { + + private static final String[] compatibleVersions = new String[] {"1.2", "1.3", "1.4", "1.5", "1.6", "1.7" }; + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterWithAllValuesSet(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.Filter filter = new ConfigXmlBuilder.Filter(Optional.of("FilterName"), Optional.of("FilterClass")); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName1"), Optional.of("paramVal1"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName2"), Optional.of("paramVal2"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName3"), Optional.of("paramVal3"))); + + Document configXml = withMinimalTags(version) + .withFilter(filter) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterValidWithoutInitParams(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.Filter filter = new ConfigXmlBuilder.Filter(Optional.of("FilterName"), Optional.of("FilterClass")); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.empty(), Optional.of("paramVal1"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName2"), Optional.of("paramVal2"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName3"), Optional.of("paramVal3"))); + + Document configXml = withMinimalTags(version) + .withFilter(filter) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterInvalidWhenMissingFilterName(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.Filter filter = new ConfigXmlBuilder.Filter(Optional.empty(), Optional.of("FilterClass")); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName1"), Optional.of("paramVal1"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName2"), Optional.of("paramVal2"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName3"), Optional.of("paramVal3"))); + + Document configXml = withMinimalTags(version) + .withFilter(filter) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterInvalidWhenMissingFilterClass(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.Filter filter = new ConfigXmlBuilder.Filter(Optional.of("FilterName"), Optional.empty()); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName1"), Optional.of("paramVal1"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName2"), Optional.of("paramVal2"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName3"), Optional.of("paramVal3"))); + + Document configXml = withMinimalTags(version) + .withFilter(filter) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterInvalidWithInitParamNameMissing(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.Filter filter = new ConfigXmlBuilder.Filter(Optional.of("FilterName"), Optional.of("FilterClass")); + + Document configXml = withMinimalTags(version) + .withFilter(filter) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterInvalidWithInitParamValueMissing(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.Filter filter = new ConfigXmlBuilder.Filter(Optional.of("FilterName"), Optional.of("FilterClass")); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName1"), Optional.empty())); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName2"), Optional.of("paramVal2"))); + filter.addInitParam(new ConfigXmlBuilder.InitParam(Optional.of("paramName3"), Optional.of("paramVal3"))); + + Document configXml = withMinimalTags(version) + .withFilter(filter) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterMappingWithUrlPattern(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.FilterMapping filterMapping = new ConfigXmlBuilder.FilterMapping(Optional.of("FilterName"), Optional.of("*.jsp"), Optional.empty()); + + Document configXml = withMinimalTags(version) + .withFilterMapping(filterMapping) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterMappingWithServletName(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.FilterMapping filterMapping = new ConfigXmlBuilder.FilterMapping(Optional.of("FilterName"), Optional.empty(), Optional.of("ServletName")); + + Document configXml = withMinimalTags(version) + .withFilterMapping(filterMapping) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterMappingWithBothUrlPatternAndServletNameFails(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.FilterMapping filterMapping = new ConfigXmlBuilder.FilterMapping(Optional.of("FilterName"), Optional.of("*.jsp"), Optional.of("ServletName")); + + Document configXml = withMinimalTags(version) + .withFilterMapping(filterMapping) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void filterMappingWithNeitherUrlPatternOrServletNameFails(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + ConfigXmlBuilder.FilterMapping filterMapping = new ConfigXmlBuilder.FilterMapping(Optional.of("FilterName"), Optional.empty(), Optional.empty()); + + Document configXml = withMinimalTags(version) + .withFilterMapping(filterMapping) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + private static Stream getCompatibleVersions() { + return Arrays.stream(compatibleVersions).map(Arguments::of); + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_3.java b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_3.java new file mode 100644 index 000000000000..3ac970072a0b --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_3.java @@ -0,0 +1,60 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmrs.module.dtd.ConfigXmlBuilder.withMinimalTags; +import static org.openmrs.module.dtd.ConfigXmlBuilder.writeToInputStream; +import static org.openmrs.module.dtd.DtdTestValidator.isValidConfigXml; + +public class ModuleConfigDTDTest_V1_3 { + + private static final String[] compatibleVersions = new String[] {"1.3", "1.4", "1.5", "1.6", "1.7" }; + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void validXmlWhenMandatoryIsSet(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withMandatory("true") + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void validXmlWhenMandatoryIsNotSet(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + private static Stream getCompatibleVersions() { + return Arrays.stream(compatibleVersions).map(Arguments::of); + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_4.java b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_4.java new file mode 100644 index 000000000000..6d3656295900 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_4.java @@ -0,0 +1,112 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmrs.module.dtd.ConfigXmlBuilder.withMinimalTags; +import static org.openmrs.module.dtd.ConfigXmlBuilder.writeToInputStream; +import static org.openmrs.module.dtd.DtdTestValidator.isValidConfigXml; + +public class ModuleConfigDTDTest_V1_4 { + + private static final String[] compatibleVersions = new String[] {"1.4", "1.5", "1.6", "1.7" }; + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void validXmlWithMultipleAwareOfModules(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List awareOfModules = new ArrayList<>(); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod1"), Optional.of("1.2.3"))); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod2"), Optional.of("1.2.4"))); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod3"), Optional.of("1.2.5"))); + + Document configXml = withMinimalTags(version) + .withAwareOfModules(awareOfModules) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + public static String toString(Document doc) { + try { + StringWriter sw = new StringWriter(); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + + transformer.transform(new DOMSource(doc), new StreamResult(sw)); + return sw.toString(); + } catch (Exception ex) { + throw new RuntimeException("Error converting to String", ex); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void validXmlWithMissingVersion(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List awareOfModules = new ArrayList<>(); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod1"), Optional.empty())); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod2"), Optional.of("1.2.4"))); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod3"), Optional.of("1.2.5"))); + + Document configXml = withMinimalTags(version) + .withAwareOfModules(awareOfModules) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void xmlFailsWithNoModules(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List awareOfModules = new ArrayList<>(); + + Document configXml = withMinimalTags(version) + .withAwareOfModules(awareOfModules) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + private static Stream getCompatibleVersions() { + return Arrays.stream(compatibleVersions).map(Arguments::of); + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_5.java b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_5.java new file mode 100644 index 000000000000..e2de5417d215 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_5.java @@ -0,0 +1,113 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmrs.module.dtd.ConfigXmlBuilder.withMinimalTags; +import static org.openmrs.module.dtd.ConfigXmlBuilder.writeToInputStream; +import static org.openmrs.module.dtd.DtdTestValidator.isValidConfigXml; + +public class ModuleConfigDTDTest_V1_5 { + + private static final String[] compatibleVersions = new String[] {"1.5", "1.6", "1.7" }; + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void validXmlWithMultipleAwareOfModules(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List awareOfModules = new ArrayList<>(); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod1"), Optional.of("1.2.3"))); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod2"), Optional.of("1.2.4"))); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod3"), Optional.of("1.2.5"))); + + Document configXml = withMinimalTags(version) + .withAwareOfModules(awareOfModules) + .withPackagesWithMappedClasses("org.openmrs.package1 org.openmrs.package2") + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + public static String toString(Document doc) { + try { + StringWriter sw = new StringWriter(); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + + transformer.transform(new DOMSource(doc), new StreamResult(sw)); + return sw.toString(); + } catch (Exception ex) { + throw new RuntimeException("Error converting to String", ex); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void validXmlWithMissingVersion(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List awareOfModules = new ArrayList<>(); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod1"), Optional.empty())); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod2"), Optional.of("1.2.4"))); + awareOfModules.add(new ConfigXmlBuilder.AwareOfModule(Optional.of("mod3"), Optional.of("1.2.5"))); + + Document configXml = withMinimalTags(version) + .withAwareOfModules(awareOfModules) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void xmlFailsWithNoModules(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List awareOfModules = new ArrayList<>(); + + Document configXml = withMinimalTags(version) + .withAwareOfModules(awareOfModules) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + private static Stream getCompatibleVersions() { + return Arrays.stream(compatibleVersions).map(Arguments::of); + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_6.java b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_6.java new file mode 100644 index 000000000000..233f1ccce32a --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_6.java @@ -0,0 +1,194 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmrs.module.dtd.ConfigXmlBuilder.withMinimalTags; +import static org.openmrs.module.dtd.ConfigXmlBuilder.writeToInputStream; +import static org.openmrs.module.dtd.DtdTestValidator.isValidConfigXml; + +public class ModuleConfigDTDTest_V1_6 { + + private static final String[] compatibleVersions = new String[] { "1.6", "1.7" }; + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void validXmlConditionalResourcesWithVersion(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List conditionalResources = new ArrayList<>(); + + ConfigXmlBuilder.ConditionalResource conditionalResource1 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource1"), Optional.of("1.2.3")); + ConfigXmlBuilder.ConditionalResource conditionalResource2 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource2"), Optional.of("1.2.4")); + + conditionalResources.add(conditionalResource1); + conditionalResources.add(conditionalResource2); + + Document configXml = withMinimalTags(version) + .withConditionalResources(conditionalResources) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void validXmlConditionalResourcesWithLoadModules(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List conditionalResources = new ArrayList<>(); + + ConfigXmlBuilder.ConditionalResource conditionalResource1 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource1"), Optional.empty()); + conditionalResource1.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod123"), Optional.of("1.0"))); + conditionalResource1.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod124"), Optional.of("1.1"))); + + ConfigXmlBuilder.ConditionalResource conditionalResource2 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource2"), Optional.empty()); + conditionalResource2.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod125"), Optional.of("2.0"))); + conditionalResource2.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod126"), Optional.of("2.1"))); + + conditionalResources.add(conditionalResource1); + conditionalResources.add(conditionalResource2); + + Document configXml = withMinimalTags(version) + .withConditionalResources(conditionalResources) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void invalidXmlWhenLoadModulesPresentWithVersion(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List conditionalResources = new ArrayList<>(); + + ConfigXmlBuilder.ConditionalResource conditionalResource1 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource1"), Optional.of("1.2.3")); + conditionalResource1.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod123"), Optional.of("1.0"))); + conditionalResource1.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod124"), Optional.of("1.1"))); + + ConfigXmlBuilder.ConditionalResource conditionalResource2 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource2"), Optional.of("1.2.4")); + conditionalResource2.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod125"), Optional.of("2.0"))); + conditionalResource2.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod126"), Optional.of("2.1"))); + + conditionalResources.add(conditionalResource1); + conditionalResources.add(conditionalResource2); + + Document configXml = withMinimalTags(version) + .withConditionalResources(conditionalResources) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void invalidXmlWhenMissingPath(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List conditionalResources = new ArrayList<>(); + + ConfigXmlBuilder.ConditionalResource conditionalResource1 = new ConfigXmlBuilder.ConditionalResource(Optional.empty(), Optional.of("1.2.3")); + ConfigXmlBuilder.ConditionalResource conditionalResource2 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource2"), Optional.of("1.2.4")); + + conditionalResources.add(conditionalResource1); + conditionalResources.add(conditionalResource2); + + Document configXml = withMinimalTags(version) + .withConditionalResources(conditionalResources) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void invalidXmlWhenBothVersionAndLoadModulesMissing(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List conditionalResources = new ArrayList<>(); + + ConfigXmlBuilder.ConditionalResource conditionalResource1 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource1"), Optional.empty()); + ConfigXmlBuilder.ConditionalResource conditionalResource2 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource2"), Optional.empty()); + + conditionalResources.add(conditionalResource1); + conditionalResources.add(conditionalResource2); + + Document configXml = withMinimalTags(version) + .withConditionalResources(conditionalResources) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void invalidXmlWhenLoadModulesMissingModuleId(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List conditionalResources = new ArrayList<>(); + + ConfigXmlBuilder.ConditionalResource conditionalResource1 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource1"), Optional.empty()); + conditionalResource1.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.empty(), Optional.of("1.0"))); + conditionalResource1.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod124"), Optional.of("1.1"))); + + conditionalResources.add(conditionalResource1); + + Document configXml = withMinimalTags(version) + .withConditionalResources(conditionalResources) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void invalidXmlWhenLoadModulesMissingVersion(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + List conditionalResources = new ArrayList<>(); + + ConfigXmlBuilder.ConditionalResource conditionalResource1 = new ConfigXmlBuilder.ConditionalResource(Optional.of("path/to/resource1"), Optional.empty()); + conditionalResource1.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod123"), Optional.empty())); + conditionalResource1.addModule(new ConfigXmlBuilder.OpenMRSModule(Optional.of("mod124"), Optional.of("1.1"))); + + conditionalResources.add(conditionalResource1); + + Document configXml = withMinimalTags(version) + .withConditionalResources(conditionalResources) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertFalse(isValidConfigXml(inputStream)); + } + } + + private static Stream getCompatibleVersions() { + return Arrays.stream(compatibleVersions).map(Arguments::of); + } +} diff --git a/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_7.java b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_7.java new file mode 100644 index 000000000000..05e4e7290c29 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/dtd/ModuleConfigDTDTest_V1_7.java @@ -0,0 +1,70 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.dtd; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openmrs.module.dtd.ConfigXmlBuilder.withMinimalTags; +import static org.openmrs.module.dtd.ConfigXmlBuilder.writeToInputStream; +import static org.openmrs.module.dtd.DtdTestValidator.isValidConfigXml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.w3c.dom.Document; + +public class ModuleConfigDTDTest_V1_7 { + + private static final String[] compatibleVersions = new String[] { "1.7" }; + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlServletWithInitParams(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Map initParams = new HashMap<>(); + initParams.put("param1", "value1"); + initParams.put("param2", "value2"); + + Document configXml = withMinimalTags(version) + .withServlet(Optional.of("ServletName"), Optional.of("ServletClass"), initParams) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + @ParameterizedTest + @MethodSource("getCompatibleVersions") + public void configXmlServletMissingInitParamsIsValid(String version) throws ParserConfigurationException, TransformerException, IOException, URISyntaxException { + Document configXml = withMinimalTags(version) + .withServlet(Optional.of("ServletName"), Optional.of("ServletClass"), Collections.emptyMap()) + .build(); + + try (InputStream inputStream = writeToInputStream(configXml)) { + assertTrue(isValidConfigXml(inputStream)); + } + } + + private static Stream getCompatibleVersions() { + return Arrays.stream(compatibleVersions).map(Arguments::of); + } +} diff --git a/api/src/test/java/org/openmrs/obs/BinaryStreamHandlerTest.java b/api/src/test/java/org/openmrs/obs/BinaryStreamHandlerTest.java index 140fe8b1d32f..f987500747e1 100644 --- a/api/src/test/java/org/openmrs/obs/BinaryStreamHandlerTest.java +++ b/api/src/test/java/org/openmrs/obs/BinaryStreamHandlerTest.java @@ -13,7 +13,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.condition.OS.WINDOWS; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -22,7 +21,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.io.TempDir; import org.openmrs.GlobalProperty; import org.openmrs.Obs; @@ -73,7 +71,6 @@ public void shouldNotSupportOtherViews() { } @Test - @DisabledOnOs(WINDOWS) public void saveObs_shouldRetrieveCorrectMimetype() throws IOException { adminService.saveGlobalProperty(new GlobalProperty( @@ -105,7 +102,7 @@ public void saveObs_shouldRetrieveCorrectMimetype() throws IOException { assertEquals(complexObs2.getComplexData().getMimeType(), mimetype); } finally { ((InputStream) complexObs1.getComplexData().getData()).close(); - ((InputStream) complexObs1.getComplexData().getData()).close(); + ((InputStream) complexObs2.getComplexData().getData()).close(); } } } diff --git a/api/src/test/java/org/openmrs/obs/MediaHandlerTest.java b/api/src/test/java/org/openmrs/obs/MediaHandlerTest.java index 2dd620696b07..ae25643014c3 100644 --- a/api/src/test/java/org/openmrs/obs/MediaHandlerTest.java +++ b/api/src/test/java/org/openmrs/obs/MediaHandlerTest.java @@ -13,7 +13,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.condition.OS.WINDOWS; import java.io.File; import java.io.FileInputStream; @@ -24,7 +23,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.io.TempDir; import org.openmrs.GlobalProperty; import org.openmrs.Obs; @@ -74,7 +72,6 @@ public void shouldNotSupportOtherViews() { } @Test - @DisabledOnOs(WINDOWS) public void saveObs_shouldRetrieveCorrectMimetype() throws IOException { adminService.saveGlobalProperty(new GlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_COMPLEX_OBS_DIR, @@ -106,7 +103,7 @@ public void saveObs_shouldRetrieveCorrectMimetype() throws IOException { assertEquals("audio/mpeg", complexObs2.getComplexData().getMimeType()); } finally { ((InputStream) complexObs1.getComplexData().getData()).close(); - ((InputStream) complexObs1.getComplexData().getData()).close(); + ((InputStream) complexObs2.getComplexData().getData()).close(); } } } diff --git a/api/src/test/java/org/openmrs/test/jupiter/StartModuleExecutionListener.java b/api/src/test/java/org/openmrs/test/jupiter/StartModuleExecutionListener.java index b04b95cfb494..7d15c3e53b0f 100644 --- a/api/src/test/java/org/openmrs/test/jupiter/StartModuleExecutionListener.java +++ b/api/src/test/java/org/openmrs/test/jupiter/StartModuleExecutionListener.java @@ -48,7 +48,7 @@ * * @since 2.4.0 */ -class StartModuleExecutionListener extends AbstractTestExecutionListener { +public class StartModuleExecutionListener extends AbstractTestExecutionListener { private static final Logger log = LoggerFactory.getLogger(StartModuleExecutionListener.class); diff --git a/api/src/test/java/org/openmrs/util/DatabaseUpdaterDatabaseIT.java b/api/src/test/java/org/openmrs/util/DatabaseUpdaterDatabaseIT.java index 1e509cbce617..f22b4aee80ef 100644 --- a/api/src/test/java/org/openmrs/util/DatabaseUpdaterDatabaseIT.java +++ b/api/src/test/java/org/openmrs/util/DatabaseUpdaterDatabaseIT.java @@ -30,7 +30,7 @@ public class DatabaseUpdaterDatabaseIT extends H2DatabaseIT { * This constant needs to be updated when adding new Liquibase update files to openmrs-core. */ - private static final int CHANGE_SET_COUNT_FOR_GREATER_THAN_2_1_X = 893; + private static final int CHANGE_SET_COUNT_FOR_GREATER_THAN_2_1_X = 897; private static final int CHANGE_SET_COUNT_FOR_2_1_X = 870; diff --git a/api/src/test/java/org/openmrs/util/LocaleUtilityTest.java b/api/src/test/java/org/openmrs/util/LocaleUtilityTest.java index 883255a1c350..140b9f263a17 100644 --- a/api/src/test/java/org/openmrs/util/LocaleUtilityTest.java +++ b/api/src/test/java/org/openmrs/util/LocaleUtilityTest.java @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. - * + * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ @@ -178,6 +178,25 @@ public void fromSpecification_shouldGetLocaleFromLanguageCodeAndCountryCode() { assertEquals(Locale.UK, LocaleUtility.fromSpecification("en_GB")); } + /** + * @see LocaleUtility#fromSpecification(String) + */ + @Test + public void fromSpecification_shouldGetLocaleFromBCP47Format() { + assertEquals(Locale.UK, LocaleUtility.fromSpecification("en-GB")); + } + + /** + * @see LocaleUtility#fromSpecification(String) + */ + @Test + public void fromSpecification_shouldReturnNullWhenLocaleFormatIsIncorrectLocaleFromBCP47Format() { + LocaleUtility.fromSpecification("en-USA"); + LocaleUtility.fromSpecification("en_USA"); + assertEquals(LocaleUtility.fromSpecification("en-USA"), null); + assertEquals(LocaleUtility.fromSpecification("en_USA"), null); + } + /** * @see LocaleUtility#fromSpecification(String) */ @@ -186,7 +205,8 @@ public void fromSpecification_shouldGetLocaleFromLanguageCodeCountryCodeAndVaria Locale locale = LocaleUtility.fromSpecification("en_US_Traditional_WIN"); assertEquals(Locale.US.getLanguage(), locale.getLanguage()); assertEquals(Locale.US.getCountry(), locale.getCountry()); - assertEquals("Traditional,WIN", locale.getDisplayVariant()); + // In Java 17 locale.getDisplayVariant() is formatted like 'Traditional, WIN' + assertEquals("Traditional,WIN", locale.getDisplayVariant().replaceAll(" ", "")); } /** diff --git a/api/src/test/java/org/openmrs/util/LocationUtilityTest.java b/api/src/test/java/org/openmrs/util/LocationUtilityTest.java index cd1fd93bcca3..297a3b457a9c 100644 --- a/api/src/test/java/org/openmrs/util/LocationUtilityTest.java +++ b/api/src/test/java/org/openmrs/util/LocationUtilityTest.java @@ -52,7 +52,8 @@ public void getUserDefaultLocation_shouldReturnTheUserSpecifiedLocationIfAnyIsSe user.setUserProperties(properties); Context.getUserService().saveUser(user); - Context.refreshAuthenticatedUser(); + Context.logout(); + authenticate(); assertEquals("Xanadu", LocationUtility.getUserDefaultLocation().getName()); } diff --git a/api/src/test/java/org/openmrs/util/OpenmrsObjectIdMatcher.java b/api/src/test/java/org/openmrs/util/OpenmrsObjectIdMatcher.java new file mode 100644 index 000000000000..41ce2149b736 --- /dev/null +++ b/api/src/test/java/org/openmrs/util/OpenmrsObjectIdMatcher.java @@ -0,0 +1,36 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.util; + +import org.hamcrest.Description; +import org.junit.internal.matchers.TypeSafeMatcher; +import org.openmrs.OpenmrsObject; +import org.openmrs.PersonName; + +import java.util.Set; + +public class OpenmrsObjectIdMatcher extends TypeSafeMatcher { + + private Integer id; + + public OpenmrsObjectIdMatcher(Integer id) { + this.id = id; + } + + @Override + public boolean matchesSafely(OpenmrsObject object) { + return id.equals(object.getId()); + } + + @Override + public void describeTo(Description description) { + description.appendText(id.toString()); + } +} diff --git a/api/src/test/java/org/openmrs/util/OpenmrsUtilTest.java b/api/src/test/java/org/openmrs/util/OpenmrsUtilTest.java index f2e66d3acc87..03d889884b89 100644 --- a/api/src/test/java/org/openmrs/util/OpenmrsUtilTest.java +++ b/api/src/test/java/org/openmrs/util/OpenmrsUtilTest.java @@ -10,6 +10,7 @@ package org.openmrs.util; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -20,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -361,8 +363,8 @@ public void validatePassword_shouldAllowPasswordToContainWhiteSpaces() { public void getDateFormat_shouldReturnAPatternWithFourYCharactersInIt() { assertEquals("MM/dd/yyyy", OpenmrsUtil.getDateFormat(Locale.US).toLocalizedPattern()); assertEquals("dd/MM/yyyy", OpenmrsUtil.getDateFormat(Locale.UK).toLocalizedPattern()); - assertEquals("tt.MM.uuuu", OpenmrsUtil.getDateFormat(Locale.GERMAN).toLocalizedPattern()); - assertEquals("dd-MM-yyyy", OpenmrsUtil.getDateFormat(new Locale("pt", "pt")).toLocalizedPattern()); + assertThat(OpenmrsUtil.getDateFormat(Locale.GERMAN).toLocalizedPattern(), anyOf(is("tt.MM.uuuu"), is("dd.MM.yyyy"))); + assertThat(OpenmrsUtil.getDateFormat(new Locale("pt", "pt")).toLocalizedPattern(), anyOf(is("dd-MM-yyyy"), is("dd/MM/yyyy"))); } /** diff --git a/api/src/test/java/org/openmrs/util/ReflectTest.java b/api/src/test/java/org/openmrs/util/ReflectTest.java index 4682c37156b3..3d3955e71578 100644 --- a/api/src/test/java/org/openmrs/util/ReflectTest.java +++ b/api/src/test/java/org/openmrs/util/ReflectTest.java @@ -150,8 +150,8 @@ public void isCollectionField_shouldReturnTrueIfGivenFieldIsCollectionAndItsElem Reflect reflect = new Reflect(OpenmrsObject.class); List allFields = Reflect.getAllFields(OpenmrsObjectImp.class); - assertEquals("subClassField", allFields.get(1).getName()); - assertTrue(reflect.isCollectionField(allFields.get(1))); + assertEquals("subClassField", allFields.get(0).getName()); + assertTrue(reflect.isCollectionField(allFields.get(0))); } /** diff --git a/api/src/test/java/org/openmrs/util/databasechange/ClassLoaderFileOpenerTest.java b/api/src/test/java/org/openmrs/util/databasechange/ClassLoaderFileOpenerTest.java deleted file mode 100644 index c85631f9b299..000000000000 --- a/api/src/test/java/org/openmrs/util/databasechange/ClassLoaderFileOpenerTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public License, - * v. 2.0. If a copy of the MPL was not distributed with this file, You can - * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under - * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. - * - * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS - * graphic logo is a trademark of OpenMRS Inc. - */ -package org.openmrs.util.databasechange; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; - -import liquibase.resource.InputStreamList; -import org.junit.jupiter.api.Test; -import org.openmrs.util.ClassLoaderFileOpener; - -public class ClassLoaderFileOpenerTest { - - @Test - public void shouldGetSingleResourceAsStream() throws IOException { - ClassLoader classLoader = mock(ClassLoader.class); - - when(classLoader.getResource(any())) - .thenReturn(getClass().getClassLoader().getResource("TestingApplicationContext.xml")); - - ClassLoaderFileOpener classLoaderFileOpener = new ClassLoaderFileOpener(classLoader); - InputStreamList inputStreamSet = classLoaderFileOpener.openStreams(null, "some path"); - - assertEquals(1, inputStreamSet.size()); - } - - @Test - public void shouldGetNoResourceAsStream() throws IOException { - ClassLoader classLoader = mock(ClassLoader.class); - - ClassLoaderFileOpener classLoaderFileOpener = new ClassLoaderFileOpener(classLoader); - InputStreamList inputStreamSet = classLoaderFileOpener.openStreams(null, ""); - - assertEquals(0, inputStreamSet.size()); - } - - @Test - public void shouldIndicateThatListIsNotSupported() throws IOException { - ClassLoader classLoader = mock(ClassLoader.class); - - ClassLoaderFileOpener classLoaderFileOpener = new ClassLoaderFileOpener(classLoader); - assertThrows(UnsupportedOperationException.class, () -> classLoaderFileOpener.list("", "", false, false, false)); - } -} diff --git a/api/src/test/java/org/openmrs/validator/PatientIdentifierValidatorTest.java b/api/src/test/java/org/openmrs/validator/PatientIdentifierValidatorTest.java index 9b4097d10ac9..96627739f7e7 100644 --- a/api/src/test/java/org/openmrs/validator/PatientIdentifierValidatorTest.java +++ b/api/src/test/java/org/openmrs/validator/PatientIdentifierValidatorTest.java @@ -14,8 +14,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.AdditionalMatchers.aryEq; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.openmrs.api.context.Context.getAuthenticatedUser; import static org.openmrs.api.context.Context.getPatientService; import static org.openmrs.validator.PatientIdentifierValidator.validateIdentifier; diff --git a/api/src/test/java/org/openmrs/validator/ProviderAttributeTypeValidatorTest.java b/api/src/test/java/org/openmrs/validator/ProviderAttributeTypeValidatorTest.java index 44adc4140d11..992dc633a9be 100644 --- a/api/src/test/java/org/openmrs/validator/ProviderAttributeTypeValidatorTest.java +++ b/api/src/test/java/org/openmrs/validator/ProviderAttributeTypeValidatorTest.java @@ -10,10 +10,13 @@ package org.openmrs.validator; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openmrs.ProviderAttributeType; +import org.openmrs.api.context.Context; import org.openmrs.test.jupiter.BaseContextSensitiveTest; import org.springframework.validation.BindException; import org.springframework.validation.Errors; @@ -23,6 +26,20 @@ */ public class ProviderAttributeTypeValidatorTest extends BaseContextSensitiveTest { + private static final String PROVIDER_ATTRIBUTE_DATA_XML = "org/openmrs/api/include/ProviderServiceTest-providerAttributes.xml"; + + /** + * Run this before each unit test in this class. This adds a bit more data to the base data that + * is done in the "@Before" method in {@link BaseContextSensitiveTest} (which is run right + * before this method). + * + * @throws Exception + */ + @BeforeEach + public void runBeforeEachTest() { + executeDataSet(PROVIDER_ATTRIBUTE_DATA_XML); + } + /** * @see ProviderAttributeTypeValidator#validate(Object, org.springframework.validation.Errors) */ @@ -40,6 +57,36 @@ public void validate_shouldPassValidationIfFieldLengthsAreCorrect() { assertFalse(errors.hasErrors()); } + /** + * @see ProviderAttributeTypeValidator#validate(Object,Errors) + */ + @Test + public void validate_shouldFailValidationIfDatatypeClassnameIsEmpty() { + ProviderAttributeType type = new ProviderAttributeType(); + type.setName("name"); + type.setDatatypeClassname(""); + type.setDescription("description"); + type.setRetireReason("retireReason"); + + Errors errors = new BindException(type, "type"); + new ProviderAttributeTypeValidator().validate(type, errors); + assertTrue(errors.hasFieldErrors("datatypeClassname")); + } + + /** + * @see ProviderAttributeTypeValidator#validate(Object,Errors) + */ + @Test + public void validate_shouldFailValidationWhenAnActiveAttributeTypeWithSameNameExists() { + assertNotNull(Context.getProviderService().getProviderAttributeTypeByName("Audit Date")); + ProviderAttributeType type = new ProviderAttributeType(); + type.setName("Audit Date"); + type.setDatatypeClassname("org.openmrs.customdatatype.datatype.DateDatatype"); + Errors errors = new BindException(type, "providerAttributeType"); + new ProviderAttributeTypeValidator().validate(type, errors); + assertTrue(errors.hasFieldErrors("name")); + } + /** * @see ProviderAttributeTypeValidator#validate(Object,Errors) */ @@ -47,14 +94,14 @@ public void validate_shouldPassValidationIfFieldLengthsAreCorrect() { public void validate_shouldFailValidationIfFieldLengthsAreNotCorrect() { ProviderAttributeType type = new ProviderAttributeType(); type - .setName("too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text"); + .setName("too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text"); type - .setDatatypeClassname("too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text"); + .setDatatypeClassname("too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text"); type.setDescription(new String(new char[655555])); type - .setPreferredHandlerClassname("too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text"); + .setPreferredHandlerClassname("too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text"); type - .setRetireReason("too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text"); + .setRetireReason("too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text too long text"); Errors errors = new BindException(type, "type"); new ProviderAttributeTypeValidator().validate(type, errors); diff --git a/api/src/test/java/org/openmrs/validator/VisitValidatorTest.java b/api/src/test/java/org/openmrs/validator/VisitValidatorTest.java old mode 100644 new mode 100755 index cf7b6c7c634a..feb9b30b570a --- a/api/src/test/java/org/openmrs/validator/VisitValidatorTest.java +++ b/api/src/test/java/org/openmrs/validator/VisitValidatorTest.java @@ -11,7 +11,6 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -201,6 +200,7 @@ public void validate_shouldFailIfTheStartDatetimeIsAfterAnyEncounter() { visit.setPatient(encounter.getPatient()); encounter.setVisit(visit); encounter.setEncounterDatetime(visit.getStartDatetime()); + Context.flushSession(); Context.getEncounterService().saveEncounter(encounter); //Set visit start date to after the encounter date. @@ -223,6 +223,7 @@ public void validate_shouldFailIfTheStopDatetimeIsBeforeAnyEncounter() { visit.setPatient(encounter.getPatient()); encounter.setVisit(visit); encounter.setEncounterDatetime(visit.getStartDatetime()); + Context.flushSession(); Context.getEncounterService().saveEncounter(encounter); //Set visit stop date to before the encounter date. diff --git a/api/src/test/resources/org/openmrs/api/db/hibernate/include/HibernateConditionDAOTestDataSet.xml b/api/src/test/resources/org/openmrs/api/db/hibernate/include/HibernateConditionDAOTestDataSet.xml index 5280e3427394..377ece4e015a 100644 --- a/api/src/test/resources/org/openmrs/api/db/hibernate/include/HibernateConditionDAOTestDataSet.xml +++ b/api/src/test/resources/org/openmrs/api/db/hibernate/include/HibernateConditionDAOTestDataSet.xml @@ -19,4 +19,16 @@ onset_date="2017-01-12 00:00:52" end_date="2017-06-12 00:10:52" verification_status="CONFIRMED" uuid="2cb6880e-2cd6-11e4-9138-a6c5e4d20fb7"/> + + + + + + diff --git a/api/src/test/resources/org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml b/api/src/test/resources/org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml index ee1d44376bb4..71261c02d13f 100644 --- a/api/src/test/resources/org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml +++ b/api/src/test/resources/org/openmrs/api/include/AdministrationServiceTest-globalproperties.xml @@ -20,4 +20,9 @@ + + + + + diff --git a/api/src/test/resources/org/openmrs/api/include/CohortServiceOrderingTest-cohort.xml b/api/src/test/resources/org/openmrs/api/include/CohortServiceOrderingTest-cohort.xml new file mode 100644 index 000000000000..dd429fa97490 --- /dev/null +++ b/api/src/test/resources/org/openmrs/api/include/CohortServiceOrderingTest-cohort.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/api/src/test/resources/org/openmrs/api/include/ConceptServiceTest-accents.xml b/api/src/test/resources/org/openmrs/api/include/ConceptServiceTest-accents.xml new file mode 100644 index 000000000000..dfe26abe6816 --- /dev/null +++ b/api/src/test/resources/org/openmrs/api/include/ConceptServiceTest-accents.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/api/src/test/resources/org/openmrs/api/include/EncounterServiceTest-initialData.xml b/api/src/test/resources/org/openmrs/api/include/EncounterServiceTest-initialData.xml index e5c0f58f5d34..58e03b3c5188 100644 --- a/api/src/test/resources/org/openmrs/api/include/EncounterServiceTest-initialData.xml +++ b/api/src/test/resources/org/openmrs/api/include/EncounterServiceTest-initialData.xml @@ -62,7 +62,8 @@ - + + @@ -72,7 +73,9 @@ - + + + diff --git a/api/src/test/resources/org/openmrs/api/include/UserServiceTest.xml b/api/src/test/resources/org/openmrs/api/include/UserServiceTest.xml index 794190e87ce1..8ca79f19d72f 100644 --- a/api/src/test/resources/org/openmrs/api/include/UserServiceTest.xml +++ b/api/src/test/resources/org/openmrs/api/include/UserServiceTest.xml @@ -63,5 +63,5 @@ - + diff --git a/api/src/test/resources/org/openmrs/include/nameSupportTestDataSet.xml b/api/src/test/resources/org/openmrs/include/nameSupportTestDataSet.xml new file mode 100644 index 000000000000..06a787296d8c --- /dev/null +++ b/api/src/test/resources/org/openmrs/include/nameSupportTestDataSet.xml @@ -0,0 +1,37 @@ + + + + + diff --git a/api/src/test/resources/org/openmrs/include/standardTestDataset.xml b/api/src/test/resources/org/openmrs/include/standardTestDataset.xml index 373f8fb3c8d9..aec298d8af7a 100644 --- a/api/src/test/resources/org/openmrs/include/standardTestDataset.xml +++ b/api/src/test/resources/org/openmrs/include/standardTestDataset.xml @@ -34,6 +34,7 @@ + @@ -328,7 +329,8 @@ - + + diff --git a/bamboo-specs/bamboo.yml b/bamboo-specs/bamboo.yml new file mode 100644 index 000000000000..7c6f41526773 --- /dev/null +++ b/bamboo-specs/bamboo.yml @@ -0,0 +1,282 @@ +--- +version: 2 +plan: + project-key: TRUNK + key: MASTER + name: OpenMRS Core Master + description: OpenMRS Core Master +stages: + - Build: + manual: false + final: false + jobs: + - Build + - Test: + manual: false + final: false + jobs: + - Unit Test + - Integration Test + - Deploy: + manual: false + final: false + jobs: + - Deploy to maven + - Deploy to docker + - Release: + manual: true + final: false + jobs: + - Release +Build: + key: BUIL + tasks: + - checkout: + force-clean-build: 'false' + description: Checkout Default Repository + - script: + interpreter: SHELL + scripts: + - |- + #!/bin/bash -eux + + set +x + + export IMAGE_DEV=${bamboo.docker.image.name}:dev + + docker login -u ${bamboo.dockerhub.username} -p ${bamboo.dockerhub.password} + + docker buildx build --pull --push --platform ${bamboo.docker.image.platforms} \ + --cache-to=type=registry,mode=max,ref=${IMAGE_DEV}-cache --cache-from ${IMAGE_DEV}-cache \ + --target dev \ + --build-arg MVN_ARGS="clean install -DskipTests" -t ${IMAGE_DEV} . + + sleep 10 + + docker pull ${IMAGE_DEV} + + echo "Inspecting image for id" + docker image inspect --format='{{index .RepoDigests 0}}' ${IMAGE_DEV} > docker-image.txt + description: Build and push dev image + - any-task: + plugin-key: com.atlassian.bamboo.plugins.variable.updater.variable-updater-generic:variable-file-reader + configuration: + filename: docker-image.txt + variable: docker.image.id + variableScope: RESULT + description: Store docker image id + artifact-subscriptions: [] +Unit Test: + key: UT + tasks: + # Checkout Task for default repository will be added implicitly during Specs import + - script: + interpreter: SHELL + scripts: + - |- + #!/bin/bash -eu + + set -x + + export IMAGE=${bamboo.docker.image.id} + + docker pull ${IMAGE} + + docker run -v m2-repo:/root/.m2/repository --rm ${IMAGE} mvn test + description: Run unit tests + artifact-subscriptions: [] +Integration Test: + key: IT + tasks: + # Checkout Task for default repository will be added implicitly during Specs import + - script: + interpreter: SHELL + scripts: + - |- + #!/bin/bash -eu + + set -x + + export IMAGE=${bamboo.docker.image.id} + + docker pull ${IMAGE} + + docker run -v m2-repo:/root/.m2/repository --rm ${IMAGE} mvn test -Pskip-default-test -Pintegration-test + description: Run integration tests + artifact-subscriptions: [] +Deploy to maven: + key: DTM + tasks: + # Checkout Task for default repository will be added implicitly during Specs import + - script: + interpreter: SHELL + scripts: + - |- + #!/bin/bash -eu + + set -x + + export IMAGE=${bamboo.docker.image.id} + + docker pull $IMAGE + + docker run -v m2-repo:/root/.m2/repository -v ~/.m2/settings.xml:/.m2/settings.xml:ro \ + --rm ${IMAGE} mvn deploy -DskipTests --settings /.m2/settings.xml + description: Deploy to maven repo + artifact-subscriptions: [] +Deploy to docker: + key: DTD + tasks: + - checkout: + force-clean-build: 'false' + description: Checkout source + - script: + interpreter: SHELL + scripts: + - |- + #!/bin/bash -eux + + set +x + + export IMAGE=${bamboo.docker.image.id} + export IMAGE_NIGHTLY=${bamboo.docker.image.name}:${bamboo.docker.image.tag} + export IMAGE_DEV=${bamboo.docker.image.name}:dev + + docker login -u ${bamboo.dockerhub.username} -p ${bamboo.dockerhub.password} + + docker pull $IMAGE + + docker buildx build --pull --push --platform ${bamboo.docker.image.platforms} \ + --cache-to=type=registry,mode=max,ref=${IMAGE_NIGHTLY}-cache \ + --cache-from=${IMAGE},${IMAGE_DEV}-cache,${IMAGE_NIGHTLY}-cache \ + --build-arg MVN_ARGS="clean install -DskipTests" -t ${IMAGE_NIGHTLY} . + description: Deploy to docker + artifact-subscriptions: [] +Release: + key: RTD + tasks: + - checkout: + force-clean-build: 'false' + description: Checkout Default Repository + - script: + interpreter: SHELL + scripts: + - |- + #!/bin/bash -eux + + set +x + + export OMRS_VERSION=${bamboo.maven.release.version} + export IMAGE=${bamboo.docker.image.name}:${OMRS_VERSION} + export DEV_IMAGE=${bamboo.docker.image.name}:${OMRS_VERSION}-dev + export BUILD_IMAGE=${bamboo.docker.image.id} + + # Set to be able to push + git remote set-url origin git@github.com:openmrs/openmrs-core.git + + docker login -u ${bamboo.dockerhub.username} -p ${bamboo.dockerhub.password} + + echo "Setting the release version" + docker pull ${BUILD_IMAGE} + docker run --rm -v m2-repo:/root/.m2/repository -v $(pwd):/openmrs_core \ + ${BUILD_IMAGE} mvn versions:set -DnewVersion=${OMRS_VERSION} -DgenerateBackupPoms=false + git commit -am "[skip-ci] Releasing ${OMRS_VERSION}" + + echo "Building the dev image" + docker buildx build --pull --push --platform ${bamboo.docker.image.platforms} \ + --cache-from ${BUILD_IMAGE} --target dev \ + --build-arg MVN_ARGS="clean install -DskipTests" -t ${DEV_IMAGE} . + + echo "Building the production image" + docker buildx build --pull --push --platform ${bamboo.docker.image.platforms} \ + --cache-from ${BUILD_IMAGE}-cache \ + --build-arg MVN_ARGS="clean install -DskipTests" -t ${IMAGE} . + + echo "Inspecting the image for id" + sleep 10 + docker pull ${IMAGE} + docker image inspect --format='{{index .RepoDigests 0}}' ${IMAGE} > docker-image.txt + + echo "Releasing to maven" + docker pull ${DEV_IMAGE} + docker run -v m2-repo:/root/.m2/repository -v ~/.m2/settings.xml:/.m2/settings.xml:ro \ + --rm ${DEV_IMAGE} mvn deploy -DskipTests --settings /.m2/settings.xml + + echo "Tagging the release in git" + git tag ${OMRS_VERSION} + git push origin ${OMRS_VERSION} + + ( + echo "Updating the main branch to a new SNAPSHOT version" + git push + docker run --rm -v m2-repo:/root/.m2/repository -v $(pwd):/openmrs_core \ + ${DEV_IMAGE} mvn versions:set -DnextSnapshot=true -DgenerateBackupPoms=false + + git commit -am "Setting new SNAPSHOT version" + git push + ) || ( + echo "Unable to update the main branch to a new SNAPSHOT version. Please update it manually if needed." + exit 0 + ) + description: Build and push images + - any-task: + plugin-key: com.atlassian.bamboo.plugins.variable.updater.variable-updater-generic:variable-file-reader + configuration: + filename: docker-image.txt + variable: docker.image.id + variableScope: RESULT + description: Store docker image id + - any-task: + plugin-key: com.atlassian.bamboo.plugins.variable.updater.variable-updater-generic:variable-extractor + configuration: + variable: maven.release.version + removeSnapshot: 'true' + variableScope: PLAN + description: Save next release version + artifact-subscriptions: [] +variables: + docker.image.name: openmrs/openmrs-core + docker.image.tag: nightly + maven.release.version: 2.7.0 +repositories: + - openmrs-core: + scope: global + - Release scripts: + scope: global +triggers: + - polling: + period: '180' + repositories: + - openmrs-core +branches: + create: manually + delete: never + link-to-jira: true +notifications: + - events: + - plan-failed + recipients: + - committers + - emails: + - dev@openmrs.org + - watchers +labels: + - platform + - test +dependencies: + require-all-stages-passing: false + enabled-for-branches: true + block-strategy: block_if_parent_has_changes + plans: [] +other: + concurrent-build-plugin: system-default +--- +version: 2 +plan: + key: TRUNK-MASTER +plan-permissions: + - roles: + - anonymous + permissions: + - view +... diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 343bf37de4db..17d70db434c1 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -22,12 +22,14 @@ services: build: target: dev context: . + cache_from: + - openmrs/openmrs-core:${TAG:-dev} ports: - "8080:8080" - "8000:8000" environment: OMRS_DEV_DEBUG_PORT: ${OMRS_DEV_DEBUG_PORT:-8000} - OMRS_CREATE_TABLES: ${OMRS_CREATE_TABLES:-false} + OMRS_CREATE_TABLES: ${OMRS_CREATE_TABLES:-true} OMRS_BUILD: ${OMRS_BUILD:-true} OMRS_BUILD_GOALS: ${OMRS_BUILD_GOALS:-} diff --git a/docker-compose.yml b/docker-compose.yml index 7e9fcee33414..557023a40808 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,10 +26,12 @@ services: - db-data:/var/lib/mysql api: - image: openmrs/openmrs-core:${TAG:-head} - build: . - depends_on: - - db + image: openmrs/openmrs-core:${TAG:-nightly} + build: + context: . + cache_from: + - openmrs/openmrs-core:${TAG:-dev} + - openmrs/openmrs-core:${TAG:-nightly} ports: - "8080:8080" environment: @@ -37,7 +39,8 @@ services: OMRS_DB_NAME: ${OMRS_DB_NAME:-openmrs} OMRS_DB_USERNAME: ${OMRS_DB_USERNAME:-openmrs} OMRS_DB_PASSWORD: ${OMRS_DB_PASSWORD:-openmrs} - OMRS_ADMIN_USER_PASSWORD: ${OMRS_ADMIN_USER_PASSWORD-Admin123} + OMRS_ADMIN_USER_PASSWORD: ${OMRS_ADMIN_USER_PASSWORD:-Admin123} + OMRS_ADMIN_PASSWORD_LOCKED: ${OMRS_ADMIN_PASSWORD_LOCKED:-true} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/openmrs"] interval: 3s diff --git a/docker-pom.xml b/docker-pom.xml new file mode 100644 index 000000000000..74bd5862876b --- /dev/null +++ b/docker-pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + org.openmrs + openmrs-docker + 1.0.0-SNAPSHOT + pom + OpenMRS Docker + POM used to setup SDK in Docker builds + https://openmrs.org + + + + openmrs-repo + OpenMRS Nexus Repository + https://mavenrepo.openmrs.org/nexus/content/repositories/public + + + + + + openmrs-repo + OpenMRS Nexus Repository + https://mavenrepo.openmrs.org/nexus/content/repositories/public + + false + + + + + + + openmrs-repo-releases + OpenMRS Nexus Releases + https://mavenrepo.openmrs.org/nexus/content/repositories/releases + + + openmrs-repo-snapshots + OpenMRS Nexus Snapshots + https://mavenrepo.openmrs.org/nexus/content/repositories/snapshots + + + diff --git a/liquibase/README.md b/liquibase/README.md index b7f6c0737c91..3ab4c27b9126 100644 --- a/liquibase/README.md +++ b/liquibase/README.md @@ -194,6 +194,20 @@ Alternatively, the corrections can be applied by running these commands: Please note that the jar file needs to be created *before* generating the Liquibase snapshots as the build process will detect that the generated files do not (yet) contain the OpenMRS license header. +#### Step 5 - Add the two PostgreSQL changesets to the liquibase-update-to-latest-version.xml file +` + Soundex extension for PostgreSQL + CREATE EXTENSION IF NOT EXISTS fuzzystrmatch SCHEMA public; +` + +` + Extension to use UUID functions with PostgreSQL and creating an alias similar to MySQL + + CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA public; + CREATE FUNCTION UUID() RETURNS UUID LANGUAGE SQL AS $$ SELECT uuid_generate_v1() $$; + +` + ### How to test Liquibase snapshots Testing the (corrected) Liquibase snapshots comprises three steps: diff --git a/liquibase/pom.xml b/liquibase/pom.xml index 83ffaf6c0218..753ea855d0de 100644 --- a/liquibase/pom.xml +++ b/liquibase/pom.xml @@ -74,7 +74,7 @@ org.liquibase liquibase-maven-plugin - 3.10.3 + 4.8.0 snapshots/${changelogfile} ${diffTypes} diff --git a/pom.xml b/pom.xml index ff438a9e3dc0..b19f73642bca 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ org.apache.velocity velocity-tools - 2.0 + 2.0.1-atlassian-2 commons-logging @@ -172,12 +172,12 @@ commons-io commons-io - 2.11.0 + 2.15.1 org.apache.commons commons-lang3 - 3.12.0 + 3.14.0 org.springframework @@ -211,7 +211,7 @@ org.javassist javassist - 3.29.2-GA + 3.30.2-GA org.hibernate @@ -269,7 +269,7 @@ org.liquibase liquibase-core - 4.4.3 + 4.8.0 ch.qos.logback @@ -384,7 +384,7 @@ commons-fileupload commons-fileupload - 1.4 + 1.5 mysql @@ -400,7 +400,7 @@ org.postgresql postgresql - 42.5.1 + 42.7.3 runtime @@ -433,11 +433,6 @@ jackson-datatype-jsr310 ${jacksonVersion} - - org.azeckoski - reflectutils - 0.9.20 - junit junit @@ -464,6 +459,12 @@ junit-vintage-engine ${junitVersion} + + org.junit.jupiter + junit-jupiter-params + ${junitVersion} + test + org.mockito mockito-junit-jupiter @@ -527,7 +528,7 @@ commons-validator commons-validator - 1.7 + 1.8.0 org.aspectj @@ -542,7 +543,7 @@ joda-time joda-time - 2.12.2 + 2.12.7 javax.annotation @@ -554,10 +555,15 @@ javax.activation 1.2.0 + + com.google.guava + guava + 33.1.0-jre + jakarta.xml.bind jakarta.xml.bind-api - 4.0.0 + 4.0.2 com.sun.mail @@ -574,13 +580,13 @@ org.testcontainers mysql - 1.17.6 + 1.19.7 test org.testcontainers postgresql - 1.17.6 + 1.19.7 test @@ -592,17 +598,17 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.2.5 org.apache.maven.plugins maven-resources-plugin - 3.3.0 + 3.3.1 org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.13.0 ${javaCompilerVersion} ${javaCompilerVersion} @@ -633,12 +639,12 @@ org.apache.maven.plugins maven-war-plugin - 3.3.2 + 3.4.0 org.codehaus.mojo buildnumber-maven-plugin - 3.0.0 + 3.2.0 revisionNumber true @@ -654,7 +660,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.3.0 + 3.5.0 com.googlecode.maven-java-formatter-plugin @@ -678,7 +684,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.2.1 + 3.3.1 checkstyle.xml @@ -694,7 +700,7 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 + 3.0.1 clean install true @@ -706,7 +712,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.0 attach-sources @@ -719,7 +725,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.1 + 3.6.3 @@ -809,12 +815,12 @@ org.sonarsource.scanner.maven sonar-maven-plugin - 3.9.1.2184 + 3.11.0.3922 org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.11 org/openmrs/** @@ -914,7 +920,7 @@ maven-assembly-plugin - 3.4.2 + 3.7.1 project @@ -924,7 +930,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M7 + 3.2.5 false false @@ -936,7 +942,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.5.0 + 3.6.1 org.apache.maven.plugins @@ -1097,6 +1103,37 @@ https://sonarcloud.io + + + + java17 + + 17 + + + + + org.mockito + mockito-core + 5.11.0 + + + net.bytebuddy + byte-buddy + 1.14.12 + + + net.bytebuddy + byte-buddy-agent + 1.14.12 + + + + @@ -1105,7 +1142,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.1 + 3.6.3 true <em><small> Generated ${TIMESTAMP} NOTE - these libraries are in active development and subject to change</small></em> @@ -1116,7 +1153,7 @@ org.apache.maven.plugins maven-jxr-plugin - 3.3.0 + 3.3.2 @@ -1169,19 +1206,19 @@ For instance, our version of Lucene is tied to the version of Hibernate Search. Similarly, our version of Hibernate Search must be compatible with the version of Hibernate and Hibernate must be compatible with our version of Spring. --> - 5.3.23 - 5.6.14.Final - 5.11.11.Final + 5.3.30 + 5.6.15.Final + 5.11.12.Final 5.5.5 - 1.9.19 - 2.14.2 - 5.9.2 - 3.12.4 + 1.9.22 + 2.17.0 + 5.10.2 + 5.11.0 2.2 1.7.36 - 2.19.0 - 4.2.0 + 2.22.1 + 4.2.1 diff --git a/startup-init.sh b/startup-init.sh index 12d970801471..e6e8deea890f 100644 --- a/startup-init.sh +++ b/startup-init.sh @@ -11,6 +11,7 @@ # Configurable environment variables OMRS_ADD_DEMO_DATA=${OMRS_ADD_DEMO_DATA:-false} OMRS_ADMIN_USER_PASSWORD=${OMRS_ADMIN_USER_PASSWORD:-Admin123} +OMRS_ADMIN_PASSWORD_LOCKED=${OMRS_ADMIN_PASSWORD_LOCKED:-false} # When set to 'true' all DB updates are applied automatically upon startup OMRS_AUTO_UPDATE_DATABASE=${OMRS_AUTO_UPDATE_DATABASE:-true} OMRS_CREATE_DATABASE_USER=${OMRS_CREATE_DATABASE_USER:-false} @@ -123,6 +124,7 @@ echo "Writing out $OMRS_SERVER_PROPERTIES_FILE" cat > $OMRS_SERVER_PROPERTIES_FILE << EOF add_demo_data=${OMRS_ADD_DEMO_DATA} admin_user_password=${OMRS_ADMIN_USER_PASSWORD} +admin_password_locked=${OMRS_ADMIN_PASSWORD_LOCKED} auto_update_database=${OMRS_AUTO_UPDATE_DATABASE} connection.driver_class=${OMRS_DB_DRIVER_CLASS} connection.username=${OMRS_DB_USERNAME} @@ -134,10 +136,33 @@ has_current_openmrs_database=${OMRS_HAS_CURRENT_OPENMRS_DATABASE} install_method=${OMRS_INSTALL_METHOD} module_web_admin=${OMRS_MODULE_WEB_ADMIN} module.allow_web_admin=${OMRS_MODULE_WEB_ADMIN} + EOF + +# Supports any custom env variable with the OMRS_EXTRA_ prefix, which translates to a property without the +# OMRS_EXTRA_ prefix. The '_' is replaced with '.' and '__' with '_'. +EXTRA_VARS=(${!OMRS_EXTRA_@}) +if [[ -n "${EXTRA_VARS+x}" ]]; then + EXTRA_PROPERTIES="" + for i in "${EXTRA_VARS[@]}" + do + : + var=$(echo "${i#OMRS_EXTRA_}" | tr [:upper:] [:lower:]) + var=${var//_/.} + var=${var//../_} + EXTRA_PROPERTIES+="${var}=${!i} \n" + done + + echo -e "$EXTRA_PROPERTIES" >> "$OMRS_SERVER_PROPERTIES_FILE" +fi + +cat "$OMRS_SERVER_PROPERTIES_FILE" + if [ -f "$OMRS_RUNTIME_PROPERTIES_FILE" ]; then - echo "Found existing runtime properties file at $OMRS_RUNTIME_PROPERTIES_FILE. Overwriting with $OMRS_SERVER_PROPERTIES_FILE" - cp "$OMRS_SERVER_PROPERTIES_FILE" "$OMRS_RUNTIME_PROPERTIES_FILE" + echo "Found existing runtime properties file at $OMRS_RUNTIME_PROPERTIES_FILE. Merging with $OMRS_SERVER_PROPERTIES_FILE" + awk -F= '!a[$1]++' "$OMRS_SERVER_PROPERTIES_FILE" "$OMRS_RUNTIME_PROPERTIES_FILE" > openmrs-merged.properties + cp openmrs-merged.properties "$OMRS_RUNTIME_PROPERTIES_FILE" + cat "$OMRS_RUNTIME_PROPERTIES_FILE" fi diff --git a/web/src/main/java/org/openmrs/module/web/ModuleServlet.java b/web/src/main/java/org/openmrs/module/web/ModuleServlet.java index 85e5451d38d8..dba50b8b7b5a 100644 --- a/web/src/main/java/org/openmrs/module/web/ModuleServlet.java +++ b/web/src/main/java/org/openmrs/module/web/ModuleServlet.java @@ -10,7 +10,9 @@ package org.openmrs.module.web; import java.io.IOException; +import java.util.Collections; import java.util.Enumeration; +import java.util.Map; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -70,7 +72,7 @@ protected void service(HttpServletRequest request, HttpServletResponse response) servlet.service(request, response); } - + /** * Internal implementation of the ServletConfig interface, to be passed to module servlets when * they are first loaded @@ -80,10 +82,13 @@ public static class SimpleServletConfig implements ServletConfig { private String name; private ServletContext servletContext; - - public SimpleServletConfig(String name, ServletContext servletContext) { + + private final Map initParameters; + + public SimpleServletConfig(String name, ServletContext servletContext, Map initParameters) { this.name = name; this.servletContext = servletContext; + this.initParameters = initParameters; } @Override @@ -99,14 +104,12 @@ public ServletContext getServletContext() { // not implemented in a module's config.xml yet @Override public String getInitParameter(String paramName) { - return null; + return initParameters.get(paramName); } - - // not implemented in a module's config.xml yet + @Override - @SuppressWarnings("unchecked") - public Enumeration getInitParameterNames() { - return null; + public Enumeration getInitParameterNames() { + return Collections.enumeration(initParameters.keySet()); } } } diff --git a/web/src/main/java/org/openmrs/module/web/WebModuleUtil.java b/web/src/main/java/org/openmrs/module/web/WebModuleUtil.java index 98b1f63f8f30..1d80566c98c8 100644 --- a/web/src/main/java/org/openmrs/module/web/WebModuleUtil.java +++ b/web/src/main/java/org/openmrs/module/web/WebModuleUtil.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.StringReader; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; @@ -96,7 +97,7 @@ private WebModuleUtil() { private static final Map MODULE_FILTERS_BY_NAME = new HashMap<>(); - private static final Deque MODULE_FILTER_MAPPINGS = new LinkedList<>(); + private static final Deque MODULE_FILTER_MAPPINGS = new ArrayDeque<>(); private static DispatcherServlet dispatcherServlet = null; @@ -440,6 +441,8 @@ public static void loadServlets(Module mod, ServletContext servletContext) { Node node = servletTags.item(i); NodeList childNodes = node.getChildNodes(); String name = "", className = ""; + + Map initParams = new HashMap<>(); for (int j = 0; j < childNodes.getLength(); j++) { Node childNode = childNodes.item(j); if ("servlet-name".equals(childNode.getNodeName())) { @@ -448,6 +451,21 @@ public static void loadServlets(Module mod, ServletContext servletContext) { } } else if ("servlet-class".equals(childNode.getNodeName()) && childNode.getTextContent() != null) { className = childNode.getTextContent().trim(); + } else if ("init-param".equals(childNode.getNodeName())) { + NodeList initParamChildren = childNode.getChildNodes(); + String paramName = null, paramValue = null; + for (int k = 0; k < initParamChildren.getLength(); k++) { + Node initParamChild = initParamChildren.item(k); + if ("param-name".equals(initParamChild.getNodeName()) && initParamChild.getTextContent() != null) { + paramName = initParamChild.getTextContent().trim(); + } else if ("param-value".equals(initParamChild.getNodeName()) && initParamChild.getTextContent() != null) { + paramValue = initParamChild.getTextContent().trim(); + } + } + + if (paramName != null && paramValue != null) { + initParams.put(paramName, paramValue); + } } } if (name.length() == 0 || className.length() == 0) { @@ -479,7 +497,7 @@ public static void loadServlets(Module mod, ServletContext servletContext) { try { log.debug("Initializing {} servlet. - {}.", name, httpServlet); - ServletConfig servletConfig = new ModuleServlet.SimpleServletConfig(name, servletContext); + ServletConfig servletConfig = new ModuleServlet.SimpleServletConfig(name, servletContext, initParams); httpServlet.init(servletConfig); } catch (Exception e) { @@ -1031,7 +1049,7 @@ public static void createDwrModulesXml(String realPath) { log.error("Failed to transorm xml source", tfe); } } - + public static String getRealPath(ServletContext servletContext) { return servletContext.getRealPath(""); } diff --git a/web/src/main/java/org/openmrs/module/web/filter/ModuleFilterMapping.java b/web/src/main/java/org/openmrs/module/web/filter/ModuleFilterMapping.java index 22671707ccc2..4c70893da552 100644 --- a/web/src/main/java/org/openmrs/module/web/filter/ModuleFilterMapping.java +++ b/web/src/main/java/org/openmrs/module/web/filter/ModuleFilterMapping.java @@ -10,9 +10,9 @@ package org.openmrs.module.web.filter; import java.io.Serializable; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; -import java.util.LinkedList; import java.util.List; import org.openmrs.module.Module; @@ -32,6 +32,8 @@ public class ModuleFilterMapping implements Serializable { private static final Logger log = LoggerFactory.getLogger(ModuleFilterMapping.class); + private static final Deque EMPTY_DEQUE = new ArrayDeque<>(0); + // Properties private Module module; @@ -267,13 +269,13 @@ public static boolean servletNameMatches(String patternToCheck, String servletNa * {@link Module} */ public static Deque retrieveFilterMappings(Module module){ - - Deque mappings = new LinkedList<>(); + Deque mappings; try { Element rootNode = module.getConfig().getDocumentElement(); NodeList mappingNodes = rootNode.getElementsByTagName("filter-mapping"); if (mappingNodes.getLength() > 0) { + mappings = new ArrayDeque<>(mappingNodes.getLength()); for (int i = 0; i < mappingNodes.getLength(); i++) { ModuleFilterMapping mapping = new ModuleFilterMapping(module); Node node = mappingNodes.item(i); @@ -294,13 +296,15 @@ public static Deque retrieveFilterMappings(Module module){ } mappings.add(mapping); } + + log.debug("Retrieved {} filter-mappings for {}: {}", mappings.size(), module, mappings); + return mappings; } } catch (Exception e) { throw new ModuleException("Unable to parse filters in module configuration.", e); } - log.debug("Retrieved {} filter-mappings for {}: {}", mappings.size(), module, mappings); - return mappings; + return EMPTY_DEQUE; } } diff --git a/web/src/main/java/org/openmrs/web/Listener.java b/web/src/main/java/org/openmrs/web/Listener.java index e23561adeff5..b5fb0975e218 100644 --- a/web/src/main/java/org/openmrs/web/Listener.java +++ b/web/src/main/java/org/openmrs/web/Listener.java @@ -9,7 +9,6 @@ */ package org.openmrs.web; -import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.LogManager; import org.openmrs.api.context.Context; import org.openmrs.logging.OpenmrsLoggingUtil; @@ -54,15 +53,14 @@ import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringReader; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Paths; import java.sql.Driver; import java.sql.DriverManager; @@ -123,6 +121,15 @@ public static boolean isSetupNeeded() { return setupNeeded; } + /** + * Boolean flag that tells if OpenMRS is started and ready to handle requests via REST. + * + * @return true if started, else false. + */ + public static boolean isOpenmrsStarted() { + return openmrsStarted; + } + /** * Get the error thrown at startup * @@ -208,7 +215,6 @@ public void contextInitialized(ServletContextEvent event) { setApplicationDataDirectory(servletContext); - loadCsrfGuardProperties(servletContext); // Try to get the runtime properties Properties props = getRuntimeProperties(); @@ -231,6 +237,8 @@ public void contextInitialized(ServletContextEvent event) { log.info("Using runtime properties file: {}", OpenmrsUtil.getRuntimePropertiesFilePathName(WebConstants.WEBAPP_NAME)); } + + loadCsrfGuardProperties(servletContext); Thread.currentThread().setContextClassLoader(OpenmrsClassLoader.getInstance()); @@ -258,28 +266,40 @@ public void contextInitialized(ServletContextEvent event) { log.error(MarkerFactory.getMarker("FATAL"), "Failed to obtain JDBC connection", e); } } - - private void loadCsrfGuardProperties(ServletContext servletContext) throws FileNotFoundException, IOException { - File file = new File(OpenmrsUtil.getApplicationDataDirectory(), "csrfguard.properties"); - InputStream inputStream = null; - try { - inputStream = new FileInputStream(file); - } - catch (FileNotFoundException ex) { - final String fileName = servletContext.getRealPath("/WEB-INF/csrfguard.properties"); - inputStream = new FileInputStream(fileName); - OutputStream outputStream = new FileOutputStream(file); - IOUtils.copy(inputStream, outputStream); - IOUtils.closeQuietly(outputStream); - IOUtils.closeQuietly(inputStream); - - //Moved to EOF by the copy operation. So open it again. - inputStream = new FileInputStream(file); - } - Properties properties = new Properties(); - properties.load(inputStream); - IOUtils.closeQuietly(inputStream); - CsrfGuard.load(properties); + + private void loadCsrfGuardProperties(ServletContext servletContext) throws IOException { + File csrfGuardFile = new File(OpenmrsUtil.getApplicationDataDirectory(), "csrfguard.properties"); + Properties csrfGuardProperties = new Properties(); + if (csrfGuardFile.exists()) { + try (InputStream csrfGuardInputStream = Files.newInputStream(csrfGuardFile.toPath())) { + csrfGuardProperties.load(csrfGuardInputStream); + } + catch (Exception e) { + log.error("Error loading csrfguard.properties file at " + csrfGuardFile.getAbsolutePath(), e); + throw e; + } + } + else { + String fileName = servletContext.getRealPath("/WEB-INF/csrfguard.properties"); + try (InputStream csrfGuardInputStream = Files.newInputStream(Paths.get(fileName))) { + csrfGuardProperties.load(csrfGuardInputStream); + } + catch (Exception e) { + log.error("Error loading csrfguard.properties file at " + fileName, e); + throw e; + } + } + + Properties runtimeProperties = getRuntimeProperties(); + if (runtimeProperties != null) { + runtimeProperties.stringPropertyNames().forEach(property -> { + if (property.startsWith("org.owasp.csrfguard")) { + csrfGuardProperties.setProperty(property, runtimeProperties.getProperty(property)); + } + }); + } + + CsrfGuard.load(csrfGuardProperties); try { //CSRFGuard by default loads properties using CsrfGuardServletContextListener diff --git a/web/src/main/java/org/openmrs/web/WebDaemon.java b/web/src/main/java/org/openmrs/web/WebDaemon.java index 7db06947f315..4e9e810c6086 100644 --- a/web/src/main/java/org/openmrs/web/WebDaemon.java +++ b/web/src/main/java/org/openmrs/web/WebDaemon.java @@ -45,7 +45,11 @@ public void run() { exceptionThrown = e; } finally { - Context.closeSession(); + try { + Context.closeSession(); + } finally { + isDaemonThread.remove(); + } } } }; diff --git a/web/src/main/java/org/openmrs/web/filter/StartupFilter.java b/web/src/main/java/org/openmrs/web/filter/StartupFilter.java index 80cff5c237d1..ef2e65df6b19 100644 --- a/web/src/main/java/org/openmrs/web/filter/StartupFilter.java +++ b/web/src/main/java/org/openmrs/web/filter/StartupFilter.java @@ -55,6 +55,7 @@ import org.openmrs.logging.OpenmrsLoggingUtil; import org.openmrs.util.LocaleUtility; import org.openmrs.util.OpenmrsUtil; +import org.openmrs.web.Listener; import org.openmrs.web.WebConstants; import org.openmrs.web.filter.initialization.InitializationFilter; import org.openmrs.web.filter.update.UpdateFilter; @@ -107,7 +108,10 @@ public abstract class StartupFilter implements Filter { @Override public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - if (skipFilter((HttpServletRequest) request)) { + if (((HttpServletRequest)request).getServletPath().equals("/health/started")) { + ((HttpServletResponse) response).setStatus(Listener.isOpenmrsStarted() ? HttpServletResponse.SC_OK : HttpServletResponse.SC_SERVICE_UNAVAILABLE); + } + else if (skipFilter((HttpServletRequest) request)) { chain.doFilter(request, response); } else { diff --git a/web/src/main/java/org/openmrs/web/filter/initialization/InitializationFilter.java b/web/src/main/java/org/openmrs/web/filter/initialization/InitializationFilter.java index 7fb1daec5180..810cfac0d5cb 100644 --- a/web/src/main/java/org/openmrs/web/filter/initialization/InitializationFilter.java +++ b/web/src/main/java/org/openmrs/web/filter/initialization/InitializationFilter.java @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. - * + * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ @@ -38,13 +38,17 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import liquibase.changelog.ChangeSet; import org.apache.commons.io.IOUtils; import org.openmrs.ImplementationId; import org.openmrs.api.APIAuthenticationException; import org.openmrs.api.PasswordException; +import org.openmrs.api.UserService; import org.openmrs.api.context.Context; import org.openmrs.api.context.ContextAuthenticationException; +import org.openmrs.api.context.UsernamePasswordCredentials; +import org.openmrs.api.impl.UserServiceImpl; import org.openmrs.liquibase.ChangeLogDetective; import org.openmrs.liquibase.ChangeLogVersionFinder; import org.openmrs.module.MandatoryModuleException; @@ -196,7 +200,7 @@ protected synchronized void setInitializationComplete(boolean initializationComp */ @Override protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpResponse) - throws IOException, ServletException { + throws IOException, ServletException { loadInstallationScriptIfPresent(); // we need to save current user language in references map since it will be used when template @@ -235,7 +239,7 @@ protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpRes result.put("executedTasks", initJob.getExecutedTasks()); result.put("completedPercentage", initJob.getCompletedPercentage()); } - + addLogLinesToResponse(result); } @@ -243,7 +247,7 @@ protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpRes writer.write(toJSONString(result)); writer.close(); } else if (InitializationWizardModel.INSTALL_METHOD_AUTO.equals(wizardModel.installMethod) - || httpRequest.getServletPath().equals("/" + AUTO_RUN_OPENMRS)) { + || httpRequest.getServletPath().equals("/" + AUTO_RUN_OPENMRS)) { autoRunOpenMRS(httpRequest); referenceMap.put("isInstallationStarted", true); httpResponse.setContentType("text/html"); @@ -284,13 +288,13 @@ protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpRes wizardModel.canWrite = runtimeProperties.canWrite(); wizardModel.databaseConnection = Context.getRuntimeProperties().getProperty("connection.url", - wizardModel.databaseConnection); + wizardModel.databaseConnection); wizardModel.currentDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", - wizardModel.currentDatabaseUsername); + wizardModel.currentDatabaseUsername); wizardModel.currentDatabasePassword = Context.getRuntimeProperties().getProperty("connection.password", - wizardModel.currentDatabasePassword); + wizardModel.currentDatabasePassword); } wizardModel.runtimePropertiesPath = runtimeProperties.getAbsolutePath(); @@ -309,18 +313,18 @@ private void loadInstallationScriptIfPresent() { wizardModel.databaseConnection = script.getProperty("connection.url", wizardModel.databaseConnection); wizardModel.databaseDriver = script.getProperty("connection.driver_class", wizardModel.databaseDriver); wizardModel.currentDatabaseUsername = script.getProperty("connection.username", - wizardModel.currentDatabaseUsername); + wizardModel.currentDatabaseUsername); wizardModel.currentDatabasePassword = script.getProperty("connection.password", - wizardModel.currentDatabasePassword); + wizardModel.currentDatabasePassword); String hasCurrentOpenmrsDatabase = script.getProperty("has_current_openmrs_database"); if (hasCurrentOpenmrsDatabase != null) { wizardModel.hasCurrentOpenmrsDatabase = Boolean.valueOf(hasCurrentOpenmrsDatabase); } wizardModel.createDatabaseUsername = script.getProperty("create_database_username", - wizardModel.createDatabaseUsername); + wizardModel.createDatabaseUsername); wizardModel.createDatabasePassword = script.getProperty("create_database_password", - wizardModel.createDatabasePassword); + wizardModel.createDatabasePassword); String createTables = script.getProperty("create_tables"); if (createTables != null) { @@ -369,14 +373,14 @@ private void clearPasswords() { */ @Override protected void doPost(HttpServletRequest httpRequest, HttpServletResponse httpResponse) - throws IOException, ServletException { + throws IOException, ServletException { String page = httpRequest.getParameter("page"); Map referenceMap = new HashMap<>(); // we need to save current user language in references map since it will be used when template // will be rendered if (httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE) != null) { referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, - httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); + httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); } // if any body has already started installation @@ -411,20 +415,20 @@ protected void doPost(HttpServletRequest httpRequest, HttpServletResponse httpRe wizardModel.canWrite = runtimeProperties.canWrite(); wizardModel.databaseConnection = Context.getRuntimeProperties().getProperty("connection.url", - wizardModel.databaseConnection); + wizardModel.databaseConnection); wizardModel.currentDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", - wizardModel.currentDatabaseUsername); + wizardModel.currentDatabaseUsername); wizardModel.currentDatabasePassword = Context.getRuntimeProperties().getProperty("connection.password", - wizardModel.currentDatabasePassword); + wizardModel.currentDatabasePassword); } wizardModel.runtimePropertiesPath = runtimeProperties.getAbsolutePath(); checkLocaleAttributes(httpRequest); referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, - httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); + httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); log.info("Locale stored in session is " + httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); httpResponse.setContentType("text/html"); @@ -433,9 +437,9 @@ protected void doPost(HttpServletRequest httpRequest, HttpServletResponse httpRe } else if (INSTALL_METHOD.equals(page)) { if (goBack(httpRequest)) { referenceMap.put(FilterUtil.REMEMBER_ATTRIBUTE, - httpRequest.getSession().getAttribute(FilterUtil.REMEMBER_ATTRIBUTE) != null); + httpRequest.getSession().getAttribute(FilterUtil.REMEMBER_ATTRIBUTE) != null); referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, - httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); + httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); renderTemplate(CHOOSE_LANG, referenceMap, httpResponse); return; } @@ -458,10 +462,11 @@ else if (SIMPLE_SETUP.equals(page)) { renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); return; } - wizardModel.databaseConnection = httpRequest.getParameter("database_connection");; + wizardModel.databaseConnection = httpRequest.getParameter("database_connection"); + ; wizardModel.createDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", - wizardModel.createDatabaseUsername); + wizardModel.createDatabaseUsername); wizardModel.createUserUsername = wizardModel.createDatabaseUsername; @@ -489,7 +494,7 @@ else if (SIMPLE_SETUP.equals(page)) { try { loadedDriverString = DatabaseUtil.loadDatabaseDriver(wizardModel.databaseConnection, - wizardModel.databaseDriver); + wizardModel.databaseDriver); } catch (ClassNotFoundException e) { errors.put(ErrorMessageConstants.ERROR_DB_DRIVER_CLASS_REQ, null); @@ -582,10 +587,10 @@ else if (DATABASE_TABLES_AND_USER.equals(page)) { if ("yes".equals(httpRequest.getParameter("current_database_user"))) { wizardModel.currentDatabaseUsername = httpRequest.getParameter("current_database_username"); checkForEmptyValue(wizardModel.currentDatabaseUsername, errors, - ErrorMessageConstants.ERROR_DB_CUR_USER_NAME_REQ); + ErrorMessageConstants.ERROR_DB_CUR_USER_NAME_REQ); wizardModel.currentDatabasePassword = httpRequest.getParameter("current_database_password"); checkForEmptyValue(wizardModel.currentDatabasePassword, errors, - ErrorMessageConstants.ERROR_DB_CUR_USER_PSWD_REQ); + ErrorMessageConstants.ERROR_DB_CUR_USER_PSWD_REQ); wizardModel.hasCurrentDatabaseUser = true; wizardModel.createDatabaseUser = false; } else { @@ -600,7 +605,7 @@ else if (DATABASE_TABLES_AND_USER.equals(page)) { if (errors.isEmpty()) { // go to next page page = InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod) ? WIZARD_COMPLETE - : OTHER_RUNTIME_PROPS; + : OTHER_RUNTIME_PROPS; } renderTemplate(page, referenceMap, httpResponse); @@ -754,7 +759,7 @@ else if (IMPLEMENTATION_ID_SETUP.equals(page)) { if (TestInstallUtil.testConnection(wizardModel.remoteUrl)) { //Check if the test module is installed by connecting to its setting page if (TestInstallUtil - .testConnection(wizardModel.remoteUrl.concat(RELEASE_TESTING_MODULE_PATH + "settings.htm"))) { + .testConnection(wizardModel.remoteUrl.concat(RELEASE_TESTING_MODULE_PATH + "settings.htm"))) { wizardModel.remoteUsername = httpRequest.getParameter("username"); wizardModel.remotePassword = httpRequest.getParameter("password"); @@ -765,8 +770,8 @@ else if (IMPLEMENTATION_ID_SETUP.equals(page)) { //check if the username and password are valid try { TestInstallUtil.getResourceInputStream( - wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "verifycredentials.htm", - wizardModel.remoteUsername, wizardModel.remotePassword); + wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "verifycredentials.htm", + wizardModel.remoteUsername, wizardModel.remotePassword); } catch (APIAuthenticationException e) { log.debug("Error generated: ", e); @@ -845,10 +850,10 @@ private void createDatabaseTask() { private void createSimpleSetup(String databaseRootPassword, String addDemoData) { setDatabaseNameIfInTestMode(); wizardModel.databaseConnection = Context.getRuntimeProperties().getProperty("connection.url", - wizardModel.databaseConnection); + wizardModel.databaseConnection); wizardModel.createDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", - wizardModel.createDatabaseUsername); + wizardModel.createDatabaseUsername); wizardModel.createUserUsername = wizardModel.createDatabaseUsername; @@ -969,21 +974,21 @@ public void checkLocaleAttributesForFirstTime(HttpServletRequest httpRequest) { * @return true/false whether it was verified or not */ private boolean verifyConnection(String connectionUsername, String connectionPassword, - String databaseConnectionFinalUrl) { + String databaseConnectionFinalUrl) { try { // verify connection //Set Database Driver using driver String Class.forName(loadedDriverString).newInstance(); Connection tempConnection = DriverManager.getConnection(databaseConnectionFinalUrl, connectionUsername, - connectionPassword); + connectionPassword); tempConnection.close(); return true; } catch (Exception e) { errors.put("User account " + connectionUsername + " does not work. " + e.getMessage() - + " See the error log for more details", - null); // TODO internationalize this + + " See the error log for more details", + null); // TODO internationalize this log.warn("Error while checking the connection user account", e); return false; } @@ -1086,7 +1091,7 @@ public void init(FilterConfig filterConfig) throws ServletException { } private void importTestDataSet(InputStream in, String connectionUrl, String connectionUsername, - String connectionPassword) throws IOException { + String connectionPassword) throws IOException { File tempFile = null; FileOutputStream fileOut = null; try { @@ -1111,7 +1116,7 @@ private void importTestDataSet(InputStream in, String connectionUrl, String conn int port = uri.getPort(); TestInstallUtil.addTestData(host, port, wizardModel.databaseName, connectionUsername, connectionPassword, - tempFile.getAbsolutePath()); + tempFile.getAbsolutePath()); } finally { IOUtils.closeQuietly(in); @@ -1155,7 +1160,8 @@ private int executeStatement(boolean silent, String user, String pw, String sql, String tempDatabaseConnection; if (sql.contains("create database")) { - tempDatabaseConnection = wizardModel.databaseConnection.replace("@DBNAME@", ""); // make this dbname agnostic so we can create the db + tempDatabaseConnection = wizardModel.databaseConnection.replace("@DBNAME@", + ""); // make this dbname agnostic so we can create the db } else { tempDatabaseConnection = wizardModel.databaseConnection.replace("@DBNAME@", wizardModel.databaseName); } @@ -1391,7 +1397,7 @@ public void run() { int result; if (sql != null) { result = executeStatement(false, wizardModel.createDatabaseUsername, - wizardModel.createDatabasePassword, sql, wizardModel.databaseName); + wizardModel.createDatabasePassword, sql, wizardModel.databaseName); } else { result = 1; } @@ -1411,7 +1417,8 @@ public void run() { setExecutingTask(WizardTask.CREATE_DB_USER); connectionUsername = wizardModel.databaseName + "_user"; if (connectionUsername.length() > 16) { - connectionUsername = wizardModel.databaseName.substring(0, 11) + "_user"; // trim off enough to leave space for _user at the end + connectionUsername = wizardModel.databaseName.substring(0, 11) + + "_user"; // trim off enough to leave space for _user at the end } connectionPassword.append(""); @@ -1428,7 +1435,7 @@ public void run() { // connect via jdbc with root user and create an openmrs user String host = "'%'"; if (wizardModel.databaseConnection.contains("localhost") - || wizardModel.databaseConnection.contains("127.0.0.1")) { + || wizardModel.databaseConnection.contains("127.0.0.1")) { host = "'localhost'"; } @@ -1440,7 +1447,7 @@ public void run() { } executeStatement(true, wizardModel.createUserUsername, wizardModel.createUserPassword, sql, - connectionUsername); + connectionUsername); if (isCurrentDatabase(DATABASE_MYSQL)) { sql = "create user '?'@" + host + " identified by '?'"; @@ -1449,7 +1456,7 @@ public void run() { } if (-1 != executeStatement(false, wizardModel.createUserUsername, wizardModel.createUserPassword, - sql, connectionUsername, connectionPassword.toString())) { + sql, connectionUsername, connectionPassword.toString())) { wizardModel.workLog.add("Created user " + connectionUsername); } else { // if error occurs stop @@ -1462,11 +1469,11 @@ public void run() { if (isCurrentDatabase(DATABASE_MYSQL)) { sql = "GRANT ALL ON `?`.* TO '?'@" + host; result = executeStatement(false, wizardModel.createUserUsername, - wizardModel.createUserPassword, sql, wizardModel.databaseName, connectionUsername); + wizardModel.createUserPassword, sql, wizardModel.databaseName, connectionUsername); } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { sql = "ALTER USER `?` WITH SUPERUSER"; result = executeStatement(false, wizardModel.createUserUsername, - wizardModel.createUserPassword, sql, connectionUsername); + wizardModel.createUserPassword, sql, connectionUsername); } // throw the user back to the main screen if this error occurs @@ -1475,7 +1482,7 @@ public void run() { return; } else { wizardModel.workLog.add("Granted user " + connectionUsername + " all privileges to database " - + wizardModel.databaseName); + + wizardModel.databaseName); } addExecutedTask(WizardTask.CREATE_DB_USER); @@ -1486,14 +1493,14 @@ public void run() { } String finalDatabaseConnectionString = wizardModel.databaseConnection.replace("@DBNAME@", - wizardModel.databaseName); + wizardModel.databaseName); finalDatabaseConnectionString = finalDatabaseConnectionString.replace("@APPLICATIONDATADIR@", - OpenmrsUtil.getApplicationDataDirectory().replace("\\", "/")); + OpenmrsUtil.getApplicationDataDirectory().replace("\\", "/")); // verify that the database connection works if (!verifyConnection(connectionUsername, connectionPassword.toString(), - finalDatabaseConnectionString)) { + finalDatabaseConnectionString)) { setMessage("Verify that the database connection works"); // redirect to setup page if we got an error reportError("Unable to connect to database", DEFAULT_PAGE); @@ -1522,9 +1529,9 @@ public void run() { runtimeProperties.put("auto_update_database", wizardModel.autoUpdateDatabase.toString()); final Encoder base64 = Base64.getEncoder(); runtimeProperties.put(OpenmrsConstants.ENCRYPTION_VECTOR_RUNTIME_PROPERTY, - new String(base64.encode(Security.generateNewInitVector()), StandardCharsets.UTF_8)); + new String(base64.encode(Security.generateNewInitVector()), StandardCharsets.UTF_8)); runtimeProperties.put(OpenmrsConstants.ENCRYPTION_KEY_RUNTIME_PROPERTY, - new String(base64.encode(Security.generateNewSecretKey()), StandardCharsets.UTF_8)); + new String(base64.encode(Security.generateNewSecretKey()), StandardCharsets.UTF_8)); Properties properties = Context.getRuntimeProperties(); properties.putAll(runtimeProperties); @@ -1550,8 +1557,8 @@ public PrintingChangeSetExecutorCallback(String message) { @Override public void executing(ChangeSet changeSet, int numChangeSetsToRun) { setMessage(message + " (" + i++ + "/" + numChangeSetsToRun + "): Author: " - + changeSet.getAuthor() + " Comments: " + changeSet.getComments() + " Description: " - + changeSet.getDescription()); + + changeSet.getAuthor() + " Comments: " + changeSet.getComments() + " Description: " + + changeSet.getDescription()); float numChangeSetsToRunFloat = (float) numChangeSetsToRun; float j = (float) i; setCompletedPercentage(Math.round(j * 100 / numChangeSetsToRunFloat)); @@ -1563,9 +1570,9 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { // use liquibase to create core data + tables try { String liquibaseSchemaFileName = changeLogVersionFinder.getLatestSchemaSnapshotFilename() - .get(); + .get(); String liquibaseCoreDataFileName = changeLogVersionFinder.getLatestCoreDataSnapshotFilename() - .get(); + .get(); setMessage("Executing " + liquibaseSchemaFileName); setExecutingTask(WizardTask.CREATE_TABLES); @@ -1573,7 +1580,7 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { log.debug("executing Liquibase file '{}' ", liquibaseSchemaFileName); DatabaseUpdater.executeChangelog(liquibaseSchemaFileName, - new PrintingChangeSetExecutorCallback("OpenMRS schema file")); + new PrintingChangeSetExecutorCallback("OpenMRS schema file")); addExecutedTask(WizardTask.CREATE_TABLES); //reset for this task @@ -1583,14 +1590,14 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { log.debug("executing Liquibase file '{}' ", liquibaseCoreDataFileName); DatabaseUpdater.executeChangelog(liquibaseCoreDataFileName, - new PrintingChangeSetExecutorCallback("OpenMRS core data file")); + new PrintingChangeSetExecutorCallback("OpenMRS core data file")); wizardModel.workLog.add("Created database tables and added core data"); addExecutedTask(WizardTask.ADD_CORE_DATA); } catch (Exception e) { reportError(ErrorMessageConstants.ERROR_DB_CREATE_TABLES_OR_ADD_DEMO_DATA, DEFAULT_PAGE, - e.getMessage()); + e.getMessage()); log.warn("Error while trying to create tables and demo data", e); } } @@ -1603,13 +1610,13 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { try { InputStream inData = TestInstallUtil.getResourceInputStream( - wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "generateTestDataSet.form", - wizardModel.remoteUsername, wizardModel.remotePassword); + wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "generateTestDataSet.form", + wizardModel.remoteUsername, wizardModel.remotePassword); setCompletedPercentage(40); setMessage("Loading imported test data..."); importTestDataSet(inData, finalDatabaseConnectionString, connectionUsername, - connectionPassword.toString()); + connectionPassword.toString()); wizardModel.workLog.add("Imported test data"); addExecutedTask(WizardTask.IMPORT_TEST_DATA); @@ -1619,8 +1626,8 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { setExecutingTask(WizardTask.ADD_MODULES); InputStream inModules = TestInstallUtil.getResourceInputStream( - wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "getModules.htm", - wizardModel.remoteUsername, wizardModel.remotePassword); + wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "getModules.htm", + wizardModel.remoteUsername, wizardModel.remotePassword); setCompletedPercentage(90); setMessage("Adding imported modules..."); @@ -1635,7 +1642,7 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { catch (APIAuthenticationException e) { log.warn("Unable to authenticate as a User with the System Developer role"); reportError(ErrorMessageConstants.UPDATE_ERROR_UNABLE_AUTHENTICATE, - TESTING_REMOTE_DETAILS_SETUP, ""); + TESTING_REMOTE_DETAILS_SETUP, ""); return; } } @@ -1656,14 +1663,14 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { log.debug("executing Liquibase file '{}' ", LIQUIBASE_DEMO_DATA); DatabaseUpdater.executeChangelog(LIQUIBASE_DEMO_DATA, - new PrintingChangeSetExecutorCallback("OpenMRS demo patients, users, and forms")); + new PrintingChangeSetExecutorCallback("OpenMRS demo patients, users, and forms")); wizardModel.workLog.add("Added demo data"); addExecutedTask(WizardTask.ADD_DEMO_DATA); } catch (Exception e) { reportError(ErrorMessageConstants.ERROR_DB_CREATE_TABLES_OR_ADD_DEMO_DATA, DEFAULT_PAGE, - e.getMessage()); + e.getMessage()); log.warn("Error while trying to add demo data", e); } } @@ -1680,21 +1687,21 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { version = changeLogVersionFinder.getLatestSnapshotVersion().get(); } else { version = changeLogDetective.getInitialLiquibaseSnapshotVersion(DatabaseUpdater.CONTEXT, - new DatabaseUpdaterLiquibaseProvider()); + new DatabaseUpdaterLiquibaseProvider()); } log.debug( - "updating the database with versions of liquibase-update-to-latest files greater than '{}'", - version); + "updating the database with versions of liquibase-update-to-latest files greater than '{}'", + version); List changelogs = changeLogVersionFinder - .getUpdateFileNames(changeLogVersionFinder.getUpdateVersionsGreaterThan(version)); + .getUpdateFileNames(changeLogVersionFinder.getUpdateVersionsGreaterThan(version)); for (String changelog : changelogs) { log.debug("applying Liquibase changelog '{}'", changelog); DatabaseUpdater.executeChangelog(changelog, - new PrintingChangeSetExecutorCallback("executing Liquibase changelog " + changelog)); + new PrintingChangeSetExecutorCallback("executing Liquibase changelog " + changelog)); } addExecutedTask(WizardTask.UPDATE_TO_LATEST); } @@ -1718,7 +1725,7 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { try { fos = new FileOutputStream(getRuntimePropertiesFile()); OpenmrsUtil.storeProperties(runtimeProperties, fos, - "Auto generated by OpenMRS initialization wizard"); + "Auto generated by OpenMRS initialization wizard"); wizardModel.workLog.add("Saved runtime properties file " + getRuntimePropertiesFile()); /* @@ -1729,12 +1736,12 @@ public void executing(ChangeSet changeSet, int numChangeSetsToRun) { */ wizardModel.workLog.add("Adjusting file posix properties to user readonly"); if (getRuntimePropertiesFile().setReadable(false, false) - && getRuntimePropertiesFile().setReadable(true)) { + && getRuntimePropertiesFile().setReadable(true)) { wizardModel.workLog - .add("Successfully adjusted RuntimePropertiesFile to disallow world to read it"); + .add("Successfully adjusted RuntimePropertiesFile to disallow world to read it"); } else { wizardModel.workLog - .add("Unable to adjust RuntimePropertiesFile to disallow world to read it"); + .add("Unable to adjust RuntimePropertiesFile to disallow world to read it"); } // don't need to catch errors here because we tested it at the beginning of the wizard } @@ -1781,8 +1788,21 @@ && getRuntimePropertiesFile().setReadable(true)) { // change the admin user password from "test" to what they input above if (wizardModel.createTables) { try { - Context.authenticate("admin", "test"); + Context.authenticate(new UsernamePasswordCredentials("admin", "test")); + + Properties props = Context.getRuntimeProperties(); + String initValue = props.getProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY); + props.setProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY, "false"); + Context.setRuntimeProperties(props); + Context.getUserService().changePassword("test", wizardModel.adminUserPassword); + + if (initValue == null) { + props.remove(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY); + } else { + props.setProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY, initValue); + } + Context.setRuntimeProperties(props); Context.logout(); } catch (ContextAuthenticationException ex) { @@ -1808,7 +1828,6 @@ && getRuntimePropertiesFile().setReadable(true)) { reportError(ErrorMessageConstants.ERROR_COMPLETE_STARTUP, DEFAULT_PAGE, e.getMessage()); return; } - // set this so that the wizard isn't run again on next page load Context.closeSession(); @@ -1828,22 +1847,22 @@ && getRuntimePropertiesFile().setReadable(true)) { // When done and the user and put in their say, call DatabaseUpdater.update(Map); // with the user's question/answer pairs log.warn( - "Unable to continue because user input is required for the db updates and we cannot do anything about that right now"); + "Unable to continue because user input is required for the db updates and we cannot do anything about that right now"); reportError(ErrorMessageConstants.ERROR_INPUT_REQ, DEFAULT_PAGE); return; } catch (MandatoryModuleException mandatoryModEx) { log.warn( - "A mandatory module failed to start. Fix the error or unmark it as mandatory to continue.", - mandatoryModEx); + "A mandatory module failed to start. Fix the error or unmark it as mandatory to continue.", + mandatoryModEx); reportError(ErrorMessageConstants.ERROR_MANDATORY_MOD_REQ, DEFAULT_PAGE, - mandatoryModEx.getMessage()); + mandatoryModEx.getMessage()); return; } catch (OpenmrsCoreModuleException coreModEx) { log.warn( - "A core module failed to start. Make sure that all core modules (with the required minimum versions) are installed and starting properly.", - coreModEx); + "A core module failed to start. Make sure that all core modules (with the required minimum versions) are installed and starting properly.", + coreModEx); reportError(ErrorMessageConstants.ERROR_CORE_MOD_REQ, DEFAULT_PAGE, coreModEx.getMessage()); return; } @@ -1886,8 +1905,8 @@ public static String loadDriver(String connection, String databaseDriver) { } catch (ClassNotFoundException e) { log.error("The given database driver class was not found. " - + "Please ensure that the database driver jar file is on the class path " - + "(like in the webapp's lib folder)"); + + "Please ensure that the database driver jar file is on the class path " + + "(like in the webapp's lib folder)"); } return loadedDriverString; @@ -1902,8 +1921,8 @@ public static String loadDriver(String connection, String databaseDriver) { private static boolean skipDatabaseSetupPage() { Properties props = OpenmrsUtil.getRuntimeProperties(WebConstants.WEBAPP_NAME); return (props != null && StringUtils.hasText(props.getProperty("connection.url")) - && StringUtils.hasText(props.getProperty("connection.username")) - && StringUtils.hasText(props.getProperty("connection.password"))); + && StringUtils.hasText(props.getProperty("connection.username")) + && StringUtils.hasText(props.getProperty("connection.password"))); } /** @@ -1914,7 +1933,7 @@ private static boolean skipDatabaseSetupPage() { */ private static boolean goBack(HttpServletRequest httpRequest) { return "Back".equals(httpRequest.getParameter("back")) - || (httpRequest.getParameter("back.x") != null && httpRequest.getParameter("back.y") != null); + || (httpRequest.getParameter("back.x") != null && httpRequest.getParameter("back.y") != null); } /** diff --git a/web/src/main/resources/openmrs-servlet.xml b/web/src/main/resources/openmrs-servlet.xml index 8379ab027a8a..3b4f15e1d056 100644 --- a/web/src/main/resources/openmrs-servlet.xml +++ b/web/src/main/resources/openmrs-servlet.xml @@ -106,6 +106,14 @@ + + + + + + + + @@ -116,7 +124,7 @@ - + diff --git a/web/src/test/java/org/openmrs/module/web/WebModuleUtilTest.java b/web/src/test/java/org/openmrs/module/web/WebModuleUtilTest.java index acf39973dfef..f9922f7774e2 100644 --- a/web/src/test/java/org/openmrs/module/web/WebModuleUtilTest.java +++ b/web/src/test/java/org/openmrs/module/web/WebModuleUtilTest.java @@ -11,26 +11,38 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileNotFoundException; import java.nio.file.Paths; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Scanner; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; +import javax.servlet.http.HttpServlet; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.openmrs.module.Module; +import org.openmrs.module.ModuleClassLoader; import org.openmrs.module.ModuleException; import org.openmrs.module.ModuleFactory; import org.openmrs.web.DispatcherServlet; @@ -45,6 +57,13 @@ public class WebModuleUtilTest { private static final String REAL_PATH = "/usr/local/apache-tomcat-7.0.27/webapps/openmrs"; + @AfterEach + public void tearDown() { + ModuleFactory.getLoadedModules().clear(); + ModuleFactory.getStartedModulesMap().clear(); + ModuleFactory.getModuleClassLoaderMap().clear(); + } + /** * @see WebModuleUtil#isModulePackageNameInTaskClass(String, String) * @throws Exception @@ -100,7 +119,7 @@ public void isModulePackageNameInTaskClass_shouldReturnFalseForEmptyPackageNames @Test public void startModule_shouldCreateDwrModulesXmlIfNotExists() throws ParserConfigurationException { // create dummy module and start it - Module mod = buildModuleForMessageTest(); + Module mod = buildModuleForMessageTest(buildModuleConfig()); ModuleFactory.getStartedModulesMap().put(mod.getModuleId(), mod); ServletContext servletContext = mock(ServletContext.class); @@ -117,8 +136,6 @@ public void startModule_shouldCreateDwrModulesXmlIfNotExists() throws ParserConf // test if dwr-modules.xml is created assertTrue(f.exists()); - - ModuleFactory.getStartedModulesMap().clear(); } /** @@ -130,7 +147,7 @@ public void startModule_shouldCreateDwrModulesXmlIfNotExists() throws ParserConf public void startModule_dwrModuleXmlshouldContainModuleInfo() throws ParserConfigurationException, FileNotFoundException { // create dummy module and start it - Module mod = buildModuleForMessageTest(); + Module mod = buildModuleForMessageTest(buildModuleConfig()); ModuleFactory.getStartedModulesMap().put(mod.getModuleId(), mod); ServletContext servletContext = mock(ServletContext.class); @@ -155,11 +172,95 @@ public void startModule_dwrModuleXmlshouldContainModuleInfo() scanner.close(); assertTrue(found); + } + + @Test + public void loadServlets_AddsServletsWithoutInitParams() + throws ParserConfigurationException, ClassNotFoundException { + String servletClassName1 = "servletClass1"; + String servletClassName2 = "servletClass2"; + + Map initParams1 = new HashMap<>(); + ServletInfo servletInfo1 = new ServletInfo("servletName1", servletClassName1, initParams1); + + Map initParams2 = new HashMap<>(); + ServletInfo servletInfo2 = new ServletInfo("servletName2", servletClassName2, initParams2); + + List servletInfos = Arrays.asList(servletInfo1, servletInfo2); + + Module mod = buildModuleForMessageTest(buildModuleConfigWithServlets(servletInfos)); + ServletContext servletContext = mock(ServletContext.class); + ModuleClassLoader moduleClassLoader = mock(ModuleClassLoader.class); + + when(moduleClassLoader.loadClass(eq(servletClassName1))).thenAnswer((Answer>) invocation -> ServletClass1.class); + when(moduleClassLoader.loadClass(eq(servletClassName2))).thenAnswer((Answer>) invocation -> ServletClass2.class); + + ModuleFactory.getModuleClassLoaderMap().put(mod, moduleClassLoader); + + WebModuleUtil.loadServlets(mod, servletContext); + + HttpServlet servlet1 = WebModuleUtil.getServlet("servletName1"); + assertNotNull(servlet1); + ServletConfig servletConfig1 = servlet1.getServletConfig(); + assertFalse(servletConfig1.getInitParameterNames().hasMoreElements()); + assertNull(servletConfig1.getInitParameter("param1")); + + HttpServlet servlet2 = WebModuleUtil.getServlet("servletName2"); + assertNotNull(servlet2); + ServletConfig servletConfig2 = servlet2.getServletConfig(); + assertFalse(servletConfig2.getInitParameterNames().hasMoreElements()); + assertNull(servletConfig2.getInitParameter("paramA")); + + WebModuleUtil.unloadServlets(mod); + } + + @Test + public void loadServlets_AddsCorrectInitParamsToConfig() + throws ParserConfigurationException, ClassNotFoundException { + String servletClassName1 = "servletClass1"; + String servletClassName2 = "servletClass2"; - ModuleFactory.getStartedModulesMap().clear(); + Map initParams1 = new HashMap<>(); + initParams1.put("param1", "value1"); + initParams1.put("param2", "value2"); + ServletInfo servletInfo1 = new ServletInfo("servletName1", servletClassName1, initParams1); + + Map initParams2 = new HashMap<>(); + initParams2.put("paramA", "valueA"); + initParams2.put("paramB", "valueB"); + ServletInfo servletInfo2 = new ServletInfo("servletName2", servletClassName2, initParams2); + + List servletInfos = Arrays.asList(servletInfo1, servletInfo2); + + Module mod = buildModuleForMessageTest(buildModuleConfigWithServlets(servletInfos)); + ServletContext servletContext = mock(ServletContext.class); + ModuleClassLoader moduleClassLoader = mock(ModuleClassLoader.class); + + when(moduleClassLoader.loadClass(eq(servletClassName1))).thenAnswer((Answer>) invocation -> ServletClass1.class); + when(moduleClassLoader.loadClass(eq(servletClassName2))).thenAnswer((Answer>) invocation -> ServletClass2.class); + + ModuleFactory.getModuleClassLoaderMap().put(mod, moduleClassLoader); + + WebModuleUtil.loadServlets(mod, servletContext); + + HttpServlet servlet1 = WebModuleUtil.getServlet("servletName1"); + assertNotNull(servlet1); + ServletConfig servletConfig1 = servlet1.getServletConfig(); + assertTrue(servletConfig1.getInitParameterNames().hasMoreElements()); + assertEquals("value1", servletConfig1.getInitParameter("param1")); + assertEquals("value2", servletConfig1.getInitParameter("param2")); + + HttpServlet servlet2 = WebModuleUtil.getServlet("servletName2"); + assertNotNull(servlet2); + ServletConfig servletConfig2 = servlet2.getServletConfig(); + assertTrue(servletConfig2.getInitParameterNames().hasMoreElements()); + assertEquals("valueA", servletConfig2.getInitParameter("paramA")); + assertEquals("valueB", servletConfig2.getInitParameter("paramB")); + + WebModuleUtil.unloadServlets(mod); } - private Module buildModuleForMessageTest() throws ParserConfigurationException { + private Module buildModuleForMessageTest(Document moduleConfig) throws ParserConfigurationException { Properties englishMessages = new Properties(); englishMessages.put("withoutPrefix", "Without prefix"); @@ -168,7 +269,7 @@ private Module buildModuleForMessageTest() throws ParserConfigurationException { mod.setMessages(new HashMap<>()); mod.getMessages().put("en", englishMessages); mod.setFile(new File("sampleFile.jar")); - mod.setConfig(buildModuleConfig()); + mod.setConfig(moduleConfig); return mod; } @@ -197,6 +298,39 @@ private Document buildModuleConfig() throws ParserConfigurationException { return doc; } + + private Document buildModuleConfigWithServlets(List servlets) throws ParserConfigurationException { + Document doc = buildModuleConfig(); + Element rootElement = doc.getDocumentElement(); + + for (ServletInfo servletInfo : servlets) { + Element servletElement = doc.createElement("servlet"); + + Element servletNameElement = doc.createElement("servlet-name"); + servletNameElement.setTextContent(servletInfo.getServletName()); + servletElement.appendChild(servletNameElement); + + Element servletClassElement = doc.createElement("servlet-class"); + servletClassElement.setTextContent(servletInfo.getServletClass()); + servletElement.appendChild(servletClassElement); + + for (Map.Entry initParam : servletInfo.getInitParams().entrySet()) { + Element initParamElement = doc.createElement("init-param"); + + Element paramNameElement = doc.createElement("param-name"); + paramNameElement.setTextContent(initParam.getKey()); + initParamElement.appendChild(paramNameElement); + + Element paramValueElement = doc.createElement("param-value"); + paramValueElement.setTextContent(initParam.getValue()); + initParamElement.appendChild(paramValueElement); + + servletElement.appendChild(initParamElement); + } + rootElement.appendChild(servletElement); + } + return doc; + } /** * @see WebModuleUtil#getModuleWebFolder(String) @@ -248,5 +382,43 @@ private static void setupMocks(boolean includeTrailingSlash) { WebModuleUtil.setDispatcherServlet(dispatcherServlet); } - + + public class ServletInfo { + private String servletName; + private String servletClass; + private Map initParams; + + public ServletInfo(String servletName, String servletClass, Map initParams) { + this.servletName = servletName; + this.servletClass = servletClass; + this.initParams = initParams; + } + + public String getServletName() { + return servletName; + } + + public void setServletName(String servletName) { + this.servletName = servletName; + } + + public String getServletClass() { + return servletClass; + } + + public void setServletClass(String servletClass) { + this.servletClass = servletClass; + } + + public Map getInitParams() { + return initParams; + } + + public void setInitParams(Map initParams) { + this.initParams = initParams; + } + } + + static class ServletClass1 extends HttpServlet {} + static class ServletClass2 extends HttpServlet {} } diff --git a/web/src/test/java/org/openmrs/web/filter/update/UpdateFilterModelTest.java b/web/src/test/java/org/openmrs/web/filter/update/UpdateFilterModelTest.java index cd61234e9e9b..d7fce315c97d 100644 --- a/web/src/test/java/org/openmrs/web/filter/update/UpdateFilterModelTest.java +++ b/web/src/test/java/org/openmrs/web/filter/update/UpdateFilterModelTest.java @@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; diff --git a/webapp/pom.xml b/webapp/pom.xml index af071ec6af4c..13c0af7e5fb3 100644 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -237,7 +237,7 @@ org.codehaus.cargo cargo-maven3-plugin - 1.9.8 + 1.10.12 tomcat9x diff --git a/webapp/src/main/resources/liquibase-demo-data.zip b/webapp/src/main/resources/liquibase-demo-data.zip index b4811b5e3533..2b8d1ceb8ccd 100644 Binary files a/webapp/src/main/resources/liquibase-demo-data.zip and b/webapp/src/main/resources/liquibase-demo-data.zip differ diff --git a/webapp/src/main/webapp/WEB-INF/web.xml b/webapp/src/main/webapp/WEB-INF/web.xml index b55d3ac32093..6f3f7a6ab20f 100644 --- a/webapp/src/main/webapp/WEB-INF/web.xml +++ b/webapp/src/main/webapp/WEB-INF/web.xml @@ -43,7 +43,7 @@ application.data.directory - +