Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions src/main/java/org/openlmis/report/service/JasperTemplateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,26 @@
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Optional;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;
import javax.imageio.ImageIO;
Expand All @@ -46,6 +56,9 @@
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.type.OrientationEnum;
import net.sf.jasperreports.engine.util.JRLoader;

import org.openlmis.report.domain.JasperTemplate;
import org.openlmis.report.domain.JasperTemplateParameter;
import org.openlmis.report.domain.JasperTemplateParameterDependency;
Expand All @@ -72,6 +85,7 @@ public class JasperTemplateService {
static final String REPORT_TYPE_PROPERTY = "reportType";
private static final String DEFAULT_REPORT_TYPE = "Consistency Report";
private static final String[] ALLOWED_FILETYPES = {"jrxml"};
private static final String CONFIG_PATH = "/config/reports/";

@Autowired
private JasperTemplateRepository jasperTemplateRepository;
Expand Down Expand Up @@ -191,6 +205,149 @@ public Map<String, BufferedImage> mapReportImagesToTemplate(JasperTemplate templ
return map;
}

/**
* Gets locale for translation resource bundle parameters.
*
* @param userLocaleString the user locale string
* @return the locale bundle parameters
* @throws MalformedURLException the malformed url exception
*/
public Map<String, Object> getLocaleBundleParameters(JasperReport parentReport,
String userLocaleString)
throws MalformedURLException {
// validate if report requires resource bundle or not
String resourceBundleName = parentReport != null ? parentReport.getResourceBundle() : null;
if (resourceBundleName == null || resourceBundleName.trim().isEmpty()) {
return Collections.emptyMap();
}

Locale userLocale;
try {
// try to parse locale param else fallback to english
userLocale = new Locale.Builder().setLanguageTag(userLocaleString).build();
} catch (Exception e) {
userLocale = Locale.ENGLISH;
}
Map<String, Object> parameters = new HashMap<>();
File resourceBundleDir = new File(CONFIG_PATH + "resourceBundles");
if (resourceBundleDir.exists() && resourceBundleDir.isDirectory()) {
URL[] urls = {resourceBundleDir.toURI().toURL()};

try (URLClassLoader externalLoader = new URLClassLoader(urls)) {
ResourceBundle externalBundle = ResourceBundle
.getBundle("report_translations", userLocale, externalLoader);

parameters.put(JRParameter.REPORT_RESOURCE_BUNDLE, externalBundle);
parameters.put(JRParameter.REPORT_LOCALE, userLocale);
} catch (IOException | MissingResourceException e) {
// No translations bundle
return Collections.emptyMap();
}
}
return parameters;
}

/**
* Gets map subreport global header parameters.
*
* @param parentReport the parent report
* @return the map subreport global header parameters
* @throws JRException the jr exception
* @throws IOException the io exception
*/
public Map<String, Object> getMapSubreportGlobalHeaderParameters(JasperReport parentReport)
throws JRException, IOException {
// validate if report requires header or not
boolean needsHeader = parentReport != null && parentReport.getParameters() != null
&& Arrays.stream(parentReport.getParameters())
.anyMatch(param -> "headerTemplate".equals(param.getName()));
if (!needsHeader) {
return Collections.emptyMap();
}

File configDir = new File(CONFIG_PATH);
if (!configDir.exists() || !configDir.isDirectory()) {
// config directory does not exist
return Collections.emptyMap();
}

String headerName;
if (OrientationEnum.LANDSCAPE.equals(parentReport.getOrientationValue())) {
headerName = "GlobalHeaderLandscape";
} else if (OrientationEnum.PORTRAIT.equals(parentReport.getOrientationValue())) {
headerName = "GlobalHeaderPortrait";
} else {
// no orientation recognized
return Collections.emptyMap();
}

Map<String, Object> parameters = new HashMap<>();
File headerFile = new File(CONFIG_PATH + headerName + ".jrxml");
if (headerFile.exists()) {
try (InputStream is = Files.newInputStream(headerFile.toPath())) {
JasperReport globalHeader = JasperCompileManager.compileReport(is);
parameters.put("headerTemplate", globalHeader);
}
} else {
return Collections.emptyMap();
}

parameters.putAll(injectDynamicHeaderParams());
return parameters;
}

/**
* Inject dynamic header params map.
*
* @return the map
* @throws IOException the io exception
*/
private Map<String, Object> injectDynamicHeaderParams() throws IOException {
Map<String, Object> parameters = new HashMap<>();
File configFile = new File(CONFIG_PATH + "header_config.properties");

if (configFile.exists()) {
Properties dynamicProps = new Properties();
try (InputStream is = Files.newInputStream(configFile.toPath())) {
dynamicProps.load(is);
}

for (String key : dynamicProps.stringPropertyNames()) {
String value = dynamicProps.getProperty(key);

if (key.endsWith("Image")) {
File imageFile = new File(CONFIG_PATH + value);
if (imageFile.exists()) {
parameters.put(key, imageFile.getAbsolutePath());
}
} else {
parameters.put(key, value);
}
}
}
return parameters;
}

/**
* Load report jasper report.
*
* @param jasperTemplate the jasper template
* @return the jasper report
* @throws ReportingException the reporting exception
*/
public JasperReport loadReport(JasperTemplate jasperTemplate) throws ReportingException {
if (jasperTemplate != null) {
try (InputStream is = new ByteArrayInputStream(jasperTemplate.getData())) {
return (JasperReport) JRLoader.loadObject(is);
} catch (JRException ex) {
throw new ReportingException(ex, ERROR_REPORTING_FILE_INVALID);
} catch (IOException ex) {
throw new ReportingException(ex, ERROR_REPORTING_IO, ex.getMessage());
}
}
return null;
}

/**
* Validate ".jrmxl" file and insert this template to database.
* Throws reporting exception if an error occurs during file validation or parsing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import static org.apache.commons.lang3.BooleanUtils.isNotFalse;
import static org.openlmis.report.i18n.JasperMessageKeys.ERROR_JASPER_TEMPLATE_NOT_FOUND;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
Expand All @@ -26,8 +28,12 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperReport;

import org.openlmis.report.domain.JasperTemplate;
import org.openlmis.report.dto.JasperTemplateDto;
import org.openlmis.report.dto.external.referencedata.UserDto;
Expand All @@ -52,6 +58,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
Expand Down Expand Up @@ -178,7 +185,8 @@ public void deleteTemplate(@PathVariable("id") UUID templateId) {
@ResponseBody
public ResponseEntity<byte[]> generateReport(
HttpServletRequest request, @PathVariable("id") UUID templateId,
@PathVariable("format") String format) throws JasperReportViewException {
@PathVariable("format") String format, @RequestParam(defaultValue = "en") String lang)
throws JasperReportViewException {
JasperTemplate template = jasperTemplateRepository.findById(templateId)
.orElseThrow(() -> new NotFoundMessageException(
new Message(ERROR_JASPER_TEMPLATE_NOT_FOUND, templateId)));
Expand All @@ -198,6 +206,18 @@ public ResponseEntity<byte[]> generateReport(
);
map.putAll(jasperTemplateService.mapReportImagesToTemplate(template));

try {
JasperReport templateReport = jasperTemplateService.loadReport(template);
map.putAll(jasperTemplateService.getLocaleBundleParameters(templateReport, lang));
map.putAll(jasperTemplateService.getMapSubreportGlobalHeaderParameters(templateReport));
} catch (ReportingException e) {
LOGGER.debug("Cannot compile template {}", template.getName());
} catch (MalformedURLException e) {
LOGGER.debug("Cannot load translation bundle for {}", template.getName());
} catch (JRException | IOException ex) {
LOGGER.debug("Cannot load GlobalHeaderTemplate for {}", template.getName());
}

map.put("format", format);
map.put("dateTimeFormat", dateTimeFormat);
map.put("dateFormat", dateFormat);
Expand Down
Loading