Miscellaneous features

Downloading Temporary Files

Sometimes users must download a file created on the fly, for example when exporting rows of a table in xls format. This can be done in many ways, for example by returning a FileSystemResource. This approach has the drawback of leaving the temporary file on the server and therefore requires an external cron job to clean the temporary folder periodically.

A better solution is to delete the temporary file just after it has been downloaded. This can be achieved by yadaWebUtil.downloadFile():

@RequestMapping("/downloadRows")
public void downloadRows(Long[] id, HttpServletResponse httpResponse) {
	File xlsFile = File.createTempFile("temp", ".xls");
	[... fetch data from DB and add to xlsFile ...]
	yadaWebUtil.downloadFile(xlsFile.toPath(), true, MediaType.APPLICATION_OCTET_STREAM_VALUE, "data.xls", httpResponse);
}

This will start the download on the browser, resulting in a "data.xls" file on the client computer. The "temp.xls" file on the server will be automatically deleted.

There’s no need to create a temporary file when returning json. See the Json section.

Shell Command Execution

Shell commands can be configured with variable parameters and executed via the YadaUtil.shellExec() method. They can be specific to an environment (development, test, production) or generic, and the executable can be different for different OS.

The following example, added to conf.webapp.prod.xml only, defines an imagemagick conversion task that can be used in every environment and different OS:

<imageConversion timeoutseconds="10"> (1)
	<executable windows="true">magick</executable> (2)
	<executable mac="true" linux="true">/usr/local/bin/magick</executable> (2)
	<arg>convert</arg>
	<arg>${FILENAMEIN}</arg> (3)
	<arg>${FILENAMEOUT}</arg>
</imageConversion>
1 A timeout in seconds can be specified, the default being 60 seconds
2 There can be more than one executable if needed
3 Dynamic parameters can be added

A different environment can have a specific configuration section with the same first tag (<imageConversion> in the above example).

Be careful when using dynamic parameters because they are implemented by the substitution map of Apache Commons Exec that has the same syntax of Commons Configuration variable interpolation, so they will be substituted if they match any valid configuration tag (even though quite unlikely).

The above configuration can be used with a code similar to the following:

Map<String, String> params = new HashMap<>();
params.put("FILENAMEIN", imageToCropFile.getAbsolutePath());
params.put("FILENAMEOUT", destinationFile.getAbsolutePath());
int exitValue = yadaUtil.shellExec("config/imageConversion", params, null);

Client Timezone

The timezone of the user is retrieved via javascript on first page load and sent to the server for storage in the HTTP Session. This is done via an ajax call at each new browser session. The timezone for unlogged users can be retrieved from the YadaConstants.SESSION_USER_TIMEZONE session attribute:

TimeZone userTimezone = (TimeZone) session.getAttribute(YadaConstants.SESSION_USER_TIMEZONE);

On user registration, the timezone is stored in the YadaRegistrationRequest and later in the YadaUserProfile when registration is confirmed. It is also refreshed at each user login so that it is always up to date. It can therefore be retrieved with

TimeZone userTimezone = userProfile.getTimezone();

This feature is totally transparent to the user. For use cases where the user must be allowed to set the timezone explicitly, the YadaUtil.getTimezones() method can be used to retrieve a list of readable timezones to present in a select. When the user sends the chosen timezone to the server, the yadaUserProfile.timezoneSetByUser flag should be set to true in order to prevent the default automatic timezone change on login. For example:

if (!formBean.getTimezoneId().equals(userProfile.getTimezone().getID())) {
	// If the timezone is different from before, set the flag
	userProfile.setTimezoneSetByUser(true);
}
userProfile.setTimezone(TimeZone.getTimeZone(formBean.getTimezoneId()));