Quando você tem um aplicativo Java que gera documentos PDF com base em um modelo CR, notará que o carregamento do modelo pela primeira vez levará algum tempo. Uma coisa que você pode fazer é carregar o template como um (Spring) @Bean. Logo você notará que após cerca de 15 minutos o documento de relatório expirará e não poderá mais ser usado para gerar PDFs.
O erro que será exibido é: com.crystaldecisions.sdk.occa.report.lib.ReportSDKException: The report document has already been closed.---- Error code:-2147467259 Error code name:failed
A única solução então é parar seu aplicativo e reiniciá-lo novamente, o que é inaceitável, claro. Outra solução é colocar o bean em um determinado escopo. O Spring, entretanto, não tem um escopo definido que se adapte a essa situação. Mas, felizmente, eles podem ser implementados com bastante facilidade.
Aqui está um exemplo de um escopo personalizado, que reinicializa os beans após 10 minutos de inatividade:
package org.springframework.beans.scope;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class TimeoutScope implements Scope {
private static final long TIMEOUT_IN_MINUTES = 10 * 60 * 1000;
private Map<String, Object> objectMap = Collections.synchronizedMap(new HashMap<String, Object>());
private Map<Object, Long> timeoutMap = Collections.synchronizedMap(new HashMap<Object, Long>());
public Object get(String name, ObjectFactory<?> objectFactory) {
if (objectMap.containsKey(name)) {
Object object = objectMap.get(name);
long timeoutValue = timeoutMap.get(object);
if (System.currentTimeMillis() >= timeoutValue) {
objectMap.remove(name);
timeoutMap.remove(object);
}
}
if (!objectMap.containsKey(name)) {
Object object = objectFactory.getObject();
objectMap.put(name, object);
}
Object object = objectMap.get(name);
timeoutMap.put(object, System.currentTimeMillis() + TIMEOUT_IN_MINUTES);
return object;
}
public Object remove(String name) {
if (objectMap.containsKey(name)) {
Object object = objectMap.get(name);
timeoutMap.remove(object);
}
return objectMap.remove(name);
}
public void registerDestructionCallback(String name, Runnable callback) {
}
public Object resolveContextualObject(String key) {
return null;
}
public String getConversationId() {
return null;
}
}
Agora você pode adicionar o escopo à sua configuração, por exemplo, fazendo o seguinte (ou escrever um equivalente em XML para ele):
@Bean
public static CustomScopeConfigurer getCustomScopes() {
CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
Map<String, Object> scopes = new HashMap<>();
scopes.put("timeout", new TimeoutScope());
customScopeConfigurer.setScopes(scopes);
return customScopeConfigurer;
}
E a partir desse momento você pode adicionar o @Scope(value = "timeout")
ao @Bean que carrega seu modelo.