Skip to content

Commit

Permalink
TRUNK-5093: AbstractHandler ensures unique naming of complex obs files
Browse files Browse the repository at this point in the history
  • Loading branch information
FloBue authored and mks-d committed May 8, 2018
1 parent 7403b90 commit 1d14c59
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 208 deletions.
86 changes: 34 additions & 52 deletions api/src/main/java/org/openmrs/obs/handler/AbstractHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.openmrs.Obs;
import org.openmrs.api.context.Context;
Expand All @@ -37,8 +36,6 @@ public class AbstractHandler {

protected NumberFormat nf;

protected SimpleDateFormat longfmt;

/**
* Constructor initializes formats for alternative file names to protect from unintentionally
* overwriting existing files.
Expand All @@ -47,7 +44,6 @@ public AbstractHandler() {
nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(0);
nf.setMinimumIntegerDigits(2);
longfmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
}

/**
Expand All @@ -59,65 +55,51 @@ public AbstractHandler() {
* @return File that the complex data should be written to
*/
public File getOutputFileToWrite(Obs obs) throws IOException {
// Get the title and remove the extension.
String title = obs.getComplexData().getTitle();
String extension = "." + getExtension(title);

// If getExtension returns the title, there was no extension
if (getExtension(title).equals(title)) {
extension = "";
}
String titleWithoutExtension = FilenameUtils.removeExtension(title);
String extension = "." + StringUtils.defaultIfEmpty(FilenameUtils.getExtension(title), "dat");
String uuid = obs.getUuid();
String filename;

File dir = OpenmrsUtil.getDirectoryInApplicationDataDirectory(Context.getAdministrationService().getGlobalProperty(
OpenmrsConstants.GLOBAL_PROPERTY_COMPLEX_OBS_DIR));
File outputfile;

// Get the output stream
if (null == title) {
String now = longfmt.format(new Date());
outputfile = new File(dir, now);
if (StringUtils.isNotBlank(titleWithoutExtension)) {
filename = titleWithoutExtension + "_" + uuid + extension;
} else {
title = title.replace(extension, "");

outputfile = new File(dir, title + extension);
filename = uuid + extension;
}

int i = 0;
String tmp;

// If the Obs does not exist, but the File does, append a two-digit
// count number to the filename and save it.
while (obs.getObsId() == null && outputfile.exists() && i < 100) {
// Remove the extension from the filename.
tmp = String.valueOf(outputfile.getAbsolutePath().replace(extension, ""));
// Append two-digit count number to the filename.
String filename = (i < 1) ? tmp + "_" + nf.format(Integer.valueOf(++i)) : tmp.replace(nf.format(Integer
.valueOf(i)), nf.format(Integer.valueOf(++i)));
// Append the extension to the filename
outputfile = new File(filename + extension);
}
File dir = OpenmrsUtil.getDirectoryInApplicationDataDirectory(
Context.getAdministrationService().getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_COMPLEX_OBS_DIR));
File outputfile = new File(dir, filename);

return outputfile;

}

/**
* Get the extension for a given filename. <br>
* If given "asdf.jpg", will return "jpg". <br>
* If given "asdf", will return "asdf". <br>
* Get the extension for a given filename if it exists, else return the filename. If there is no
* filename in the input string, "raw" is returned.
*
* If given "asdf.jpg", will return "jpg".
* If given "asdf", will return "asdf".
* If given "" or "a/b/c/" will return "raw".
*
* @param filename
* @return the filepart after the period in the given filename
* @return the part after the period in the given filename, the filename, or "raw"
* @deprecated since 2.1.3 use {@link org.apache.commons.io.FilenameUtils#getExtension(String)}
* instead.
*/
@Deprecated
public String getExtension(String filename) {
String[] filenameParts = filename.split("\\.");
String result = FilenameUtils.getExtension(filename);

log.debug("titles length: " + filenameParts.length);

String extension = (filenameParts.length < 2) ? filenameParts[0] : filenameParts[filenameParts.length - 1];
extension = StringUtils.isNotEmpty(extension) ? extension : "raw";
if (StringUtils.isEmpty(result)) {
result = FilenameUtils.getBaseName(filename);

if (StringUtils.isEmpty(result)) {
result = "raw";
}
}

return extension;
return result;
}

/**
Expand Down Expand Up @@ -153,8 +135,8 @@ public boolean purgeComplexData(Obs obs) {
return true;
}

log.warn("Could not delete complex data object for obsId=" + obs.getObsId() + " located at "
+ file.getAbsolutePath());
log.warn(
"Could not delete complex data object for obsId=" + obs.getObsId() + " located at " + file.getAbsolutePath());
return false;
}

Expand All @@ -167,8 +149,8 @@ public boolean purgeComplexData(Obs obs) {
public static File getComplexDataFile(Obs obs) {
String[] names = obs.getValueComplex().split("\\|");
String filename = names.length < 2 ? names[0] : names[names.length - 1];
File dir = OpenmrsUtil.getDirectoryInApplicationDataDirectory(Context.getAdministrationService().getGlobalProperty(
OpenmrsConstants.GLOBAL_PROPERTY_COMPLEX_OBS_DIR));
File dir = OpenmrsUtil.getDirectoryInApplicationDataDirectory(
Context.getAdministrationService().getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_COMPLEX_OBS_DIR));
return new File(dir, filename);
}

Expand Down
18 changes: 10 additions & 8 deletions api/src/test/java/org/openmrs/api/ObsServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -642,14 +642,7 @@ public void saveObs_shouldCreateNewFileFromComplexDataForNewObs() {
ObsService os = Context.getObsService();
ConceptService cs = Context.getConceptService();
AdministrationService as = Context.getAdministrationService();

// make sure the file isn't there to begin with
File complexObsDir = OpenmrsUtil.getDirectoryInApplicationDataDirectory(as
.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_COMPLEX_OBS_DIR));
File createdFile = new File(complexObsDir, "nameOfFile.txt");
if (createdFile.exists())
createdFile.delete();


// the complex data to put onto an obs that will be saved
Reader input = new CharArrayReader("This is a string to save to a file".toCharArray());
ComplexData complexData = new ComplexData("nameOfFile.txt", input);
Expand All @@ -661,6 +654,15 @@ public void saveObs_shouldCreateNewFileFromComplexDataForNewObs() {
Obs obsToSave = new Obs(new Person(1), questionConcept, new Date(), new Location(1));
obsToSave.setComplexData(complexData);

// make sure the file isn't there to begin with
String filename = "nameOfFile_" + obsToSave.getUuid() + ".txt";
File complexObsDir = OpenmrsUtil.getDirectoryInApplicationDataDirectory(as
.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_COMPLEX_OBS_DIR));
File createdFile = new File(complexObsDir, filename);
if (createdFile.exists()) {
createdFile.delete();
}

try {
os.saveObs(obsToSave, null);

Expand Down
166 changes: 166 additions & 0 deletions api/src/test/java/org/openmrs/obs/AbstractHandlerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/**
* 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 https://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at https://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.obs;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.openmrs.Obs;
import org.openmrs.api.APIException;
import org.openmrs.api.AdministrationService;
import org.openmrs.api.context.Context;
import org.openmrs.obs.handler.AbstractHandler;
import org.openmrs.util.OpenmrsUtil;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ AbstractHandler.class, OpenmrsUtil.class, Context.class })

public class AbstractHandlerTest {

private final String FILENAME = "mytxtfile.txt";

private final AbstractHandler handler = new AbstractHandler();

@Mock
private AdministrationService administrationService;

@Rule
public TemporaryFolder complexObsTestFolder = new TemporaryFolder();

@Before
public void initializeContext() throws APIException, IOException {
mockStatic(Context.class);
when(Context.getAdministrationService()).thenReturn(administrationService);
when(administrationService.getGlobalProperty(any())).thenReturn(complexObsTestFolder.newFolder().getAbsolutePath());
}

@Test
public void getOutputFileToWrite_shouldNeverOverwritePreviousFiles() throws IOException {
String content1 = "A";
String content2 = "B";

File previousFile = null;
File currentFile = null;

for (int i = 0; i <= 101; i++) {
String currentData = (i % 2 == 0) ? content1 : content2;

ComplexData complexData = new ComplexData(FILENAME, currentData);

Obs obs = new Obs();
obs.setComplexData(complexData);

currentFile = handler.getOutputFileToWrite(obs);

try (BufferedWriter fout = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(currentFile), StandardCharsets.UTF_8))) {
fout.write(currentData);
}

try (BufferedReader fin = new BufferedReader(
new InputStreamReader(new FileInputStream(currentFile), StandardCharsets.UTF_8))) {
String readData = fin.readLine();
assertEquals(readData, currentData);
}

if (i > 0) {
assertFalse(FileUtils.contentEquals(previousFile, currentFile));
}

previousFile = currentFile;
}
}

@Test
public void getOutputFileToWrite_shouldCorrectlyNameTitledFileWithExtension() throws IOException, ParseException {
ComplexData complexDataWithTitle = new ComplexData(FILENAME, null);

Obs obsWithTitle = new Obs();
obsWithTitle.setComplexData(complexDataWithTitle);

File titledFile = handler.getOutputFileToWrite(obsWithTitle);
titledFile.createNewFile();

String[] nameWithTitle = titledFile.getName().split("_|\\.");

String titlePart = nameWithTitle[0];
String uuidPartWithTitle = nameWithTitle[1];
String extensionPart = nameWithTitle[2];

assertEquals(titlePart, FilenameUtils.removeExtension(FILENAME));
assertEquals(extensionPart, "txt");
assertEquals(uuidPartWithTitle, obsWithTitle.getUuid());
}

@Test
public void getOutputFileToWrite_shouldCorrectlyNameTitledFileWithoutExtension() throws IOException, ParseException {
ComplexData complexDataWithoutExtension = new ComplexData(FilenameUtils.removeExtension(FILENAME), null);

Obs obsWithoutExtension = new Obs();
obsWithoutExtension.setComplexData(complexDataWithoutExtension);

File extensionlessFile = handler.getOutputFileToWrite(obsWithoutExtension);
extensionlessFile.createNewFile();

String[] nameWithoutExtension = extensionlessFile.getName().split("_|\\.");

String titlePartExtensionless = nameWithoutExtension[0];
String uuidPartExtensionless = nameWithoutExtension[1];
String extensionPartExtensionless = nameWithoutExtension[2];

assertEquals(titlePartExtensionless, FilenameUtils.removeExtension(FILENAME));
assertEquals(extensionPartExtensionless, "dat");
assertEquals(uuidPartExtensionless, obsWithoutExtension.getUuid());
}

@Test
public void getOutputFileToWrite_shouldCorrectlyNameNullTitledFile() throws IOException, ParseException {
ComplexData complexDataWithNullTitle = new ComplexData(null, null);

Obs obsWithNullTitle = new Obs();
obsWithNullTitle.setComplexData(complexDataWithNullTitle);

File nullTitleFile = handler.getOutputFileToWrite(obsWithNullTitle);
nullTitleFile.createNewFile();

String[] nameWithNullTitle = nullTitleFile.getName().split("\\.");

String uuidPartWithNullTitle = nameWithNullTitle[0];
String extensionPartWithNullTitle = nameWithNullTitle[1];

assertEquals(extensionPartWithNullTitle, "dat");
assertEquals(uuidPartWithNullTitle, obsWithNullTitle.getUuid());
}

}
Loading

0 comments on commit 1d14c59

Please sign in to comment.