com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.EngineServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.EngineServiceImpl.java

Source

/*
 * Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License  as
 * published by the Free Software Foundation, either version 3 of  the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero  General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public  License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl;

import com.jaspersoft.jasperserver.api.JSException;
import com.jaspersoft.jasperserver.api.JSExceptionWrapper;
import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
import com.jaspersoft.jasperserver.api.common.domain.ValidationResult;
import com.jaspersoft.jasperserver.api.common.domain.impl.ExecutionContextImpl;
import com.jaspersoft.jasperserver.api.common.domain.impl.ValidationDetailImpl;
import com.jaspersoft.jasperserver.api.common.domain.impl.ValidationResultImpl;
import com.jaspersoft.jasperserver.api.common.util.TimeZoneContextHolder;
import com.jaspersoft.jasperserver.api.engine.common.domain.Request;
import com.jaspersoft.jasperserver.api.engine.common.domain.Result;
import com.jaspersoft.jasperserver.api.engine.common.service.BuiltInParameterProvider;
import com.jaspersoft.jasperserver.api.engine.common.service.EngineService;
import com.jaspersoft.jasperserver.api.engine.common.service.IDataSourceAwareQueryManipulator;
import com.jaspersoft.jasperserver.api.engine.common.service.IQueryManipulator;
import com.jaspersoft.jasperserver.api.engine.common.service.JRXMLFixer;
import com.jaspersoft.jasperserver.api.engine.common.service.ReportExecutionStatusInformation;
import com.jaspersoft.jasperserver.api.engine.common.service.ReportExecutionStatusSearchCriteria;
import com.jaspersoft.jasperserver.api.engine.common.service.ReportInputControlInformation;
import com.jaspersoft.jasperserver.api.engine.common.service.ReportInputControlValuesInformation;
import com.jaspersoft.jasperserver.api.engine.common.service.ReportInputControlsInformation;
import com.jaspersoft.jasperserver.api.engine.common.service.SchedulerReportExecutionStatusSearchCriteria;
import com.jaspersoft.jasperserver.api.engine.common.service.SecurityContextProvider;
import com.jaspersoft.jasperserver.api.engine.jasperreports.common.ReportExecuter;
import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.CompositeReportExecutionListener;
import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.ReportExecutionListener;
import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.ReportExecutionListenerFactory;
import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.ReportUnitRequest;
import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.ReportUnitRequestBase;
import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.ReportUnitResult;
import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.TrialReportUnitRequest;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.DataCacheProvider;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.InputControlsInfoExtractor;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.DataSourceServiceFactory;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.DefaultProtectionDomainProvider;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.InputControlLabelResolver;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.JRQueryExecuterAdapter;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.JarsClassLoader;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.MessageSourceLoader;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.ProtectionDomainProvider;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.ReportInputControlValuesInformationLoader;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.RepositoryCacheMap;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.RepositoryCacheMap.CacheObject;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.RepositoryCacheMap.ObjectCache;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.RepositoryContext;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.RepositoryResourceClassLoader;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.RepositoryResourceKey;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.RepositoryUtil;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.ResourceCollector;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.repo.RepositoryURLHandlerFactory;
import com.jaspersoft.jasperserver.api.engine.scheduling.quartz.ReportExecutionJob;
import com.jaspersoft.jasperserver.api.logging.audit.context.AuditContext;
import com.jaspersoft.jasperserver.api.logging.audit.domain.AuditEvent;
import com.jaspersoft.jasperserver.api.logging.diagnostic.domain.DiagnosticAttribute;
import com.jaspersoft.jasperserver.api.logging.diagnostic.helper.DiagnosticAttributeBuilder;
import com.jaspersoft.jasperserver.api.logging.diagnostic.service.Diagnostic;
import com.jaspersoft.jasperserver.api.logging.diagnostic.service.DiagnosticCallback;
import com.jaspersoft.jasperserver.api.metadata.common.domain.FileResource;
import com.jaspersoft.jasperserver.api.metadata.common.domain.FileResourceData;
import com.jaspersoft.jasperserver.api.metadata.common.domain.InputControl;
import com.jaspersoft.jasperserver.api.metadata.common.domain.InputControlsContainer;
import com.jaspersoft.jasperserver.api.metadata.common.domain.ListOfValues;
import com.jaspersoft.jasperserver.api.metadata.common.domain.MemoryDataContainer;
import com.jaspersoft.jasperserver.api.metadata.common.domain.Query;
import com.jaspersoft.jasperserver.api.metadata.common.domain.Resource;
import com.jaspersoft.jasperserver.api.metadata.common.domain.ResourceContainer;
import com.jaspersoft.jasperserver.api.metadata.common.domain.ResourceLookup;
import com.jaspersoft.jasperserver.api.metadata.common.domain.ResourceReference;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryCache;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryCacheableItem;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryUnsecure;
import com.jaspersoft.jasperserver.api.metadata.data.cache.DataCacheSnapshot;
import com.jaspersoft.jasperserver.api.metadata.data.cache.DataSnapshotPersistentMetadata;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.DataView;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.ReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.ReportUnit;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceService;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceServiceFactory;
import com.jaspersoft.jasperserver.api.metadata.user.domain.User;
import com.jaspersoft.jasperserver.api.metadata.view.domain.FilterCriteria;

import net.sf.ehcache.pool.SizeOfEngine;
import net.sf.ehcache.pool.impl.DefaultSizeOfEngine;
import net.sf.jasperreports.data.cache.DataSnapshotException;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRQuery;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRTemplate;
import net.sf.jasperreports.engine.JRVirtualizer;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.fill.AsynchronousFillHandle;
import net.sf.jasperreports.engine.fill.AsynchronousFilllListener;
import net.sf.jasperreports.engine.fill.BaseFillHandle;
import net.sf.jasperreports.engine.fill.FillHandle;
import net.sf.jasperreports.engine.fill.FillListener;
import net.sf.jasperreports.engine.fill.JRFillContext;
import net.sf.jasperreports.engine.fill.JRParameterDefaultValuesEvaluator;
import net.sf.jasperreports.engine.query.JRQueryExecuter;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.util.JRResourcesUtil;
import net.sf.jasperreports.engine.util.JRSaver;
import net.sf.jasperreports.engine.xml.JRXmlLoader;
import net.sf.jasperreports.engine.xml.JRXmlTemplateLoader;
import net.sf.jasperreports.engine.xml.JRXmlWriter;
import net.sf.jasperreports.repo.JasperDesignCache;
import net.sf.jasperreports.web.servlets.AsyncJasperPrintAccessor;
import net.sf.jasperreports.web.servlets.JasperPrintAccessor;
import net.sf.jasperreports.web.servlets.SimpleJasperPrintAccessor;
import org.apache.commons.collections.OrderedMap;
import org.apache.commons.collections.map.ReferenceMap;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.JarFile;

/**
 *
 * @author Teodor Danciu (teodord@users.sourceforge.net)
 * @version $Id: EngineServiceImpl.java 48307 2014-08-15 21:38:37Z ichan $
 */
public class EngineServiceImpl implements EngineService, ReportExecuter, CompiledReportProvider,
        InternalReportCompiler, InitializingBean, Diagnostic {
    protected static final Log log = LogFactory.getLog(EngineServiceImpl.class);
    protected static final Log valueQueryLog = LogFactory.getLog("valueQueryLog");

    private DataSourceServiceFactory dataSourceServiceFactories;
    protected RepositoryUnsecure unsecureRepository;
    protected RepositoryService repository;
    private SecurityContextProvider securityContextProvider;
    private ProtectionDomainProvider reportJarsProtectionDomainProvider = new DefaultProtectionDomainProvider();
    private String reportParameterLabelKeyPrefix;
    private IQueryManipulator queryManipulator = null;

    private boolean autoUpdateJRXMLs;
    private List<JRXMLFixer> jrxmlFixerList;

    private RepositoryCacheMap tempJarFiles;
    private RepositoryCache compiledReportsCache;
    private final ReferenceMap jarsClassLoaderCache;
    private final ReferenceMap resourcesClassLoaderCache;
    private RepositoryCacheableItem cacheableCompiledReports;
    private RepositoryContextManager repositoryContextManager;
    private List builtInParameterProviders = new ArrayList();

    private AuditContext auditContext;
    protected boolean recordSizeof = false;

    protected InputControlsInfoExtractor inputControlsInfoExtractor = new InputControlsInfoRoutingExtractor();
    //TODO consider injecting and using ReportLoadingService

    /*** THE FOLLOWING PARAMETER ARE USED FOR CANCEL EXECUTIONS **/
    private final ThreadLocal<ReportExecutionStatus> currentExecutionStatus = new ThreadLocal<ReportExecutionStatus>();

    @javax.annotation.Resource
    private Map<String, ReportExecutionStatus> engineExecutions;

    private Executor syncReportExecutorService = SynchronousExecutor.INSTANCE;//default value

    private Executor asyncReportExecutorService = Executors.newCachedThreadPool();//default value

    private List<ReportExecutionListenerFactory> reportExecutionListenerFactories;

    private DataCacheProvider dataCacheProvider;

    private List<ReportDataParameterContributor> dataParameterContributors;

    private JasperReportsContext jasperReportsContext;

    private AtomicLong startSyncReportExecutionCount = new AtomicLong();
    private AtomicLong startAsyncReportExecutionCount = new AtomicLong();
    private AtomicLong endSyncReportExecutionCount = new AtomicLong();
    private AtomicLong endAsyncReportExecutionCount = new AtomicLong();
    private AtomicLong errorReportExecutionCount = new AtomicLong();

    public EngineServiceImpl() {
        jarsClassLoaderCache = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.SOFT);
        resourcesClassLoaderCache = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.SOFT);
        cacheableCompiledReports = new CacheableCompiledReports(this);
    }

    public void afterPropertiesSet() throws Exception {
        if (repositoryContextManager == null) {
            repositoryContextManager = new DefaultRepositoryContextManager(repository, this);
        }

        createJarFilesCache();
    }

    /**
     * @return Returns the queryManipulator.
     */
    public IQueryManipulator getQueryManipulator() {
        return queryManipulator;
    }

    /**
     * @param queryManipulator The queryManipulator to set.
     */
    public void setQueryManipulator(IQueryManipulator queryManipulator) {
        this.queryManipulator = queryManipulator;
    }

    /**
     *
     */
    public RepositoryService getRepositoryService() {
        return repository;
    }

    public void setRepositoryService(RepositoryService repository) {
        this.repository = repository;
    }

    public RepositoryCache getCompiledReportsCache() {
        return compiledReportsCache;
    }

    public void setCompiledReportsCache(RepositoryCache compiledReportsCache) {
        this.compiledReportsCache = compiledReportsCache;
    }

    public RepositoryCacheableItem getCacheableCompiledReports() {
        return cacheableCompiledReports;
    }

    public void setCacheableCompiledReports(RepositoryCacheableItem cacheableCompiledReports) {
        this.cacheableCompiledReports = cacheableCompiledReports;
    }

    public RepositoryContextManager getRepositoryContextManager() {
        return repositoryContextManager;
    }

    public void setRepositoryContextManager(RepositoryContextManager repositoryContextManager) {
        this.repositoryContextManager = repositoryContextManager;
    }

    public AuditContext getAuditContext() {
        return auditContext;
    }

    public void setAuditContext(AuditContext auditContext) {
        this.auditContext = auditContext;
    }

    public List getBuiltInParameterProviders() {
        return builtInParameterProviders;
    }

    public void setBuiltInParameterProviders(List builtInParameterProviders) {
        this.builtInParameterProviders = builtInParameterProviders;
    }

    public void setInputControlsInfoExtractor(InputControlsInfoExtractor inputControlsInfoExtractor) {
        this.inputControlsInfoExtractor = inputControlsInfoExtractor;
    }

    public ProtectionDomainProvider getReportJarsProtectionDomainProvider() {
        return reportJarsProtectionDomainProvider;
    }

    public void setReportJarsProtectionDomainProvider(ProtectionDomainProvider protectionDomainProvider) {
        this.reportJarsProtectionDomainProvider = protectionDomainProvider;
    }

    /**
     * <code>Diagnostic</code> implementation - collecting info about currently executed reports.
     * @author Oleg Gavavka, vsabadosh
     */
    public Map<DiagnosticAttribute, DiagnosticCallback> getDiagnosticData() {
        return new DiagnosticAttributeBuilder()
                .addDiagnosticAttribute(DiagnosticAttributeBuilder.RUNNING_REPORT_COUNT,
                        new DiagnosticCallback<Long>() {
                            @Override
                            public Long getDiagnosticAttributeValue() {
                                return (startAsyncReportExecutionCount.get() + startSyncReportExecutionCount.get())
                                        - (endAsyncReportExecutionCount.get() + endSyncReportExecutionCount.get());
                            }
                        })
                .addDiagnosticAttribute(DiagnosticAttributeBuilder.RUNNING_REPORTS_LIST,
                        new DiagnosticCallback<List<String>>() {
                            @Override
                            public List<String> getDiagnosticAttributeValue() {
                                List<String> reportUrisWithTimeExecutions = new ArrayList<String>();
                                for (ReportExecutionStatus status : engineExecutions.values()) {
                                    Long executionTime = System.currentTimeMillis()
                                            - status.getStartTime().getTime();
                                    reportUrisWithTimeExecutions
                                            .add(status.getReportURI() + "=" + executionTime / 1000);
                                }
                                return reportUrisWithTimeExecutions;
                            }
                        })
                .addDiagnosticAttribute(DiagnosticAttributeBuilder.RUNNING_REPORT_ASYNCTASKCOUNT,
                        new DiagnosticCallback<Long>() {
                            @Override
                            public Long getDiagnosticAttributeValue() {
                                if (asyncReportExecutorService instanceof ThreadPoolExecutor) {
                                    return startAsyncReportExecutionCount.get();
                                }
                                return 0L;
                            }
                        })
                .addDiagnosticAttribute(DiagnosticAttributeBuilder.RUNNING_REPORT_ASYNCPOOLSIZE,
                        new DiagnosticCallback<Integer>() {
                            @Override
                            public Integer getDiagnosticAttributeValue() {
                                if (asyncReportExecutorService instanceof ThreadPoolExecutor) {
                                    return ((ThreadPoolExecutor) (asyncReportExecutorService)).getPoolSize();
                                }
                                return 0;
                            }
                        })
                .addDiagnosticAttribute(DiagnosticAttributeBuilder.RUNNING_REPORT_ASYNCACTIVETASKCOUNT,
                        new DiagnosticCallback<Long>() {
                            @Override
                            public Long getDiagnosticAttributeValue() {
                                return startAsyncReportExecutionCount.get() - endAsyncReportExecutionCount.get();

                            }
                        })
                .addDiagnosticAttribute(DiagnosticAttributeBuilder.RUNNING_REPORT_SYNCTASKCOUNT,
                        new DiagnosticCallback<Long>() {
                            @Override
                            public Long getDiagnosticAttributeValue() {

                                return startSyncReportExecutionCount.get();
                            }
                        })
                .addDiagnosticAttribute(DiagnosticAttributeBuilder.RUNNING_REPORT_ERRORCOUNT,
                        new DiagnosticCallback<Long>() {
                            @Override
                            public Long getDiagnosticAttributeValue() {

                                return errorReportExecutionCount.get();
                            }
                        })
                .addDiagnosticAttribute(DiagnosticAttributeBuilder.RUNNING_REPORT_COUNT_CUML,
                        new DiagnosticCallback<Long>() {
                            @Override
                            public Long getDiagnosticAttributeValue() {

                                return startAsyncReportExecutionCount.get() + startSyncReportExecutionCount.get();
                            }
                        })
                .build();
    }

    protected final class TempJarFileCacheObject implements ObjectCache {
        public boolean isValid(Object o) {
            return true;
        }

        public Object create(ExecutionContext context, FileResource res) {
            try {
                File tempFile = File.createTempFile("report_jar", ".jar");
                tempFile.deleteOnExit();

                if (log.isInfoEnabled()) {
                    log.info("Created temp jar file \"" + tempFile.getPath() + "\" for resource \""
                            + res.getURIString() + "\"");
                }

                byte[] data = getFileResourceData(context, res);
                OutputStream fileOut = new BufferedOutputStream(new FileOutputStream(tempFile));
                try {
                    fileOut.write(data);
                    fileOut.flush();
                } finally {
                    fileOut.close();
                }

                JarFile jarFile = new JarFile(tempFile);

                return jarFile;
            } catch (IOException e) {
                log.error(e, e);
                throw new JSExceptionWrapper(e);
            }
        }

        public void release(Object o) {
            dispose((JarFile) o);
        }
    }

    protected void createJarFilesCache() {
        this.tempJarFiles = new RepositoryCacheMap(this.repository, this.repositoryContextManager,
                new TempJarFileCacheObject());
    }

    protected InputStream getFileResourceDataStream(ExecutionContext context, FileResource fileResource) {
        InputStream data;
        if (fileResource.hasData()) {
            data = fileResource.getDataStream();
        } else {
            FileResourceData resourceData = repository.getResourceData(context, fileResource.getURIString());
            data = resourceData.getDataStream();
        }
        return data;
    }

    protected CacheObject getCacheJarFile(ExecutionContext context, FileResource jar, boolean cache) {
        return tempJarFiles.cache(context, jar, cache);
    }

    protected byte[] getFileResourceData(ExecutionContext context, FileResource fileResource) {
        byte[] data;
        if (fileResource.hasData()) {
            data = fileResource.getData();
        } else {
            FileResourceData resourceData = repository.getResourceData(context, fileResource.getURIString());
            data = resourceData.getData();
        }
        return data;
    }

    public DataSourceServiceFactory getDataSourceServiceFactories() {
        return dataSourceServiceFactories;
    }

    public void setDataSourceServiceFactories(DataSourceServiceFactory dataSourceServiceFactories) {
        this.dataSourceServiceFactories = dataSourceServiceFactories;
    }

    /**
     * create a cancelable process
     */
    public Result execute(ExecutionContext context, Request request) {
        ReportUnitRequestBase reportUnitRequest = (ReportUnitRequestBase) request;
        return reportUnitRequest.execute(context, this);
    }

    protected void startExecution(Request request) {
        if (log.isDebugEnabled()) {
            log.debug("Launching execution " + request.getId());
        }

        ReportExecutionStatus status;
        if (request instanceof ReportUnitRequest) {
            status = new ReportExecutionStatus(request, ((ReportUnitRequest) request).getPropertyMap());
            status.setReportURI(((ReportUnitRequest) request).getReportUnitUri());
            status.setOwner(securityContextProvider.getContextUser());

        } else
            status = new ReportExecutionStatus(request);

        engineExecutions.put(request.getId(), status);
        currentExecutionStatus.set(status);
    }

    protected void endExecution(Request request) {
        engineExecutions.remove(request.getId());
        currentExecutionStatus.set(null);

        if (log.isDebugEnabled()) {
            log.debug("Ended execution " + request.getId());
        }
    }

    protected ReportExecutionStatus currentExecutionStatus() {
        return currentExecutionStatus.get();
    }

    protected String currentExecutionId() {
        ReportExecutionStatus status = currentExecutionStatus();
        return status == null ? "Not tracked" : status.getRequestId();
    }

    protected JasperReportsContext getEffectiveJasperReportsContext() {
        JasperReportsContext jrContext = getJasperReportsContext();
        if (jrContext == null) {
            jrContext = DefaultJasperReportsContext.getInstance();
        }
        return jrContext;
    }

    /**
     *
     */
    public void exportToPdf(ExecutionContext context, String reportUnitURI, Map exportParameters) {
        ReportUnit reportUnit = getRepositoryResource(context, reportUnitURI, ReportUnit.class);
        RepositoryContextHandle repositoryContextHandle = setThreadRepositoryContext(context, null, reportUnitURI);
        try {
            OrigContextClassLoader origContext = setContextClassLoader(context, reportUnit, false,
                    repositoryContextHandle);
            try {
                exportParameters.put(JRExporterParameter.URL_HANDLER_FACTORY,
                        RepositoryURLHandlerFactory.getInstance());
                JRPdfExporter exporter = createPdfExporter(context);
                exporter.setParameters(exportParameters);
                exporter.exportReport();
            } finally {
                revert(origContext);
            }
        } catch (JRException e) {
            log.error("Error while exporting report to PDF", e);
            throw new JSExceptionWrapper(e);
        } finally {
            resetThreadRepositoryContext(repositoryContextHandle);
        }
    }

    protected JRPdfExporter createPdfExporter(ExecutionContext context) {
        return new JRPdfExporter(getEffectiveJasperReportsContext());
    }

    public RepositoryContextHandle setThreadRepositoryContext(ExecutionContext context,
            ResourceContainer reportUnit, String reportUnitURI) {
        // resources loading in the repository context should only need execute permissions,
        // so make sure that the execution context has the magic attribute to signal that perms change
        context = getRuntimeExecutionContext(context);
        if (repositoryContextManager == null)
            return null;
        if (reportUnitURI == null)
            return null;
        return repositoryContextManager.setRepositoryContext(context, reportUnitURI, reportUnit);
    }

    public void resetThreadRepositoryContext(RepositoryContextHandle repositoryContextHandle) {
        if (repositoryContextHandle != null) {
            repositoryContextHandle.unset();
        }
    }

    protected ReportUnitResult fillReport(ExecutionContext context, ReportUnitRequestBase request,
            ReportUnit reportUnit, boolean inMemoryUnit) {
        ReportExecutionListener executionListener = createReportExecutionListener(request);

        boolean asynchronous = request.isAsynchronous();
        ReportFiller filler = createReportFiller(asynchronous);
        filler.setJasperReportsContext(request.getJasperReportsContext());

        Executor executor = getReportExecutor(asynchronous);
        if (log.isDebugEnabled()) {
            log.debug("Running report " + reportUnit.getURIString() + " on " + executor);
        }

        executionListener.init();
        ReportFill reportFill = new ReportFill(context, request, reportUnit, inMemoryUnit, filler,
                executionListener);
        executor.execute(reportFill);

        // blocking for the result
        JasperPrintAccessor reportAccessor;
        try {
            reportAccessor = filler.getResult();
        } catch (RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            throw new JSException(e);
        }

        if (log.isDebugEnabled()) {
            log.debug("Returning fill result " + reportAccessor);
        }

        Map params = request.getReportParameters();
        JRVirtualizer virtualizer = params == null ? null
                : ((JRVirtualizer) params.get(JRParameter.REPORT_VIRTUALIZER));

        ReportUnitResult result = new ReportUnitResult(reportUnit.getURIString(), reportAccessor, virtualizer);
        result.setRequestId(request.getId());
        result.setDataTimestamp(filler.getDataTimestamp());
        result.setReportContext(request.getReportContext());
        result.setPaginated(filler.isPaginated());
        return result;
    }

    protected ReportExecutionListener createReportExecutionListener(ReportUnitRequestBase request) {
        List<ReportExecutionListener> listeners = new LinkedList<ReportExecutionListener>();
        for (ReportExecutionListenerFactory listenerFactory : getReportExecutionListenerFactories()) {
            ReportExecutionListener listener = listenerFactory.createListener(request);
            if (listener != null) {
                listeners.add(listener);
            }
        }
        return CompositeReportExecutionListener.asListener(listeners);
    }

    protected ReportFiller createReportFiller(boolean asynchronous) {
        ReportFiller filler;
        if (asynchronous) {
            filler = new AsynchronousReportFiller();
        } else {
            filler = new SynchronousReportFiller();
        }
        return filler;
    }

    protected Executor getReportExecutor(boolean asynchronous) {
        Executor executor;
        if (asynchronous) {
            executor = asyncReportExecutorService;
        } else {
            executor = syncReportExecutorService;
        }
        return executor;
    }

    protected abstract class ReportFiller {

        private final Lock resultLock;
        private final Condition resultSync;
        private JasperReportsContext jasperReportsContext;
        private JasperPrintAccessor result;
        private Throwable error;
        private Date dataTimestamp;
        protected boolean asyncable;
        private boolean paginated = true;

        protected ReportFiller() {
            resultLock = new ReentrantLock();
            resultSync = resultLock.newCondition();
        }

        protected boolean hasResult() {
            resultLock.lock();
            try {
                return result != null || error != null;
            } finally {
                resultLock.unlock();
            }
        }

        protected void setResult(JasperPrintAccessor result, boolean paginated, boolean skipWhenSet) {
            resultLock.lock();
            try {
                if (this.result == null || !skipWhenSet) {
                    if (log.isDebugEnabled()) {
                        log.debug("Report fill result available " + result);
                    }

                    this.result = result;
                    this.paginated = paginated;
                    resultSync.signalAll();
                }
            } finally {
                resultLock.unlock();
            }
        }

        public void setError(Throwable error) {
            resultLock.lock();
            try {
                if (log.isDebugEnabled()) {
                    log.debug("Report fill error", error);
                }

                this.error = error;
                resultSync.signalAll();
            } finally {
                resultLock.unlock();
            }
        }

        public boolean hasError() {
            return error != null;
        }

        public JasperPrintAccessor getResult() throws Throwable {
            resultLock.lock();
            try {
                while (result == null && error == null) {
                    resultSync.await();
                }

                if (error != null) {
                    throw error;
                }

                return result;
            } catch (InterruptedException e) {
                throw new JSException(e);
            } finally {
                resultLock.unlock();
            }
        }

        protected JasperReportsContext getFillJasperReportsContext() {
            JasperReportsContext fillContext = jasperReportsContext;
            if (fillContext == null) {
                fillContext = getEffectiveJasperReportsContext();
            }
            return fillContext;
        }

        public abstract void fillReport(JasperReport report, Map<String, Object> parameters,
                JRDataSource dataSource) throws JRException;

        public Date getDataTimestamp() {
            return dataTimestamp;
        }

        public void setDataTimestamp(Date dataTimestamp) {
            this.dataTimestamp = dataTimestamp;
        }

        public void setAsyncable(boolean asyncable) {
            this.asyncable = asyncable;
        }

        public JasperReportsContext getJasperReportsContext() {
            return jasperReportsContext;
        }

        public void setJasperReportsContext(JasperReportsContext jasperReportsContext) {
            this.jasperReportsContext = jasperReportsContext;
        }

        public boolean isPaginated() {
            return paginated;
        }
    }

    protected class SynchronousReportFiller extends ReportFiller {

        public SynchronousReportFiller() {
            //NOP
        }

        public void fillReport(JasperReport report, Map<String, Object> parameters, JRDataSource dataSource)
                throws JRException {
            JasperReportsContext jasperReportsContext = getFillJasperReportsContext();
            FillResultListener fillResult = EngineServiceImpl.this.fillReport(jasperReportsContext, report,
                    parameters, dataSource);

            // set the result
            SimpleJasperPrintAccessor result = new SimpleJasperPrintAccessor(fillResult.getJasperPrint());
            setResult(result, fillResult.isPaginated(), false);
        }
    }

    protected class AsynchronousReportFiller extends ReportFiller {

        public void fillReport(JasperReport report, Map<String, Object> parameters, JRDataSource dataSource)
                throws JRException {
            // creating a synchronous fill handler since the async fork already happened
            // we need a fill handler for AsyncJasperPrintAccessor
            checkRequestCanceled();

            if (log.isDebugEnabled()) {
                log.debug("Filling report for request " + currentExecutionId());
            }

            JasperReportsContext jasperReportsContext = getFillJasperReportsContext();
            final SynchronousFillHandle fillHandle = new SynchronousFillHandle(jasperReportsContext, report,
                    parameters, dataSource);
            final AsyncJasperPrintAccessor result = new AsyncJasperPrintAccessor(fillHandle);

            if (asyncable) {
                // setting the result once we have the first page so that fillReport can return.
                // we are waiting for the first page in order to catch common exceptions that occur
                // while initializing the report and data source.
                fillHandle.addFillListener(new FillListener() {
                    public void pageGenerated(JasperPrint jasperPrint, int pageIndex) {
                        if (pageIndex == 0) {
                            setResult(result, fillHandle.isPaginated(), false);
                        }
                    }

                    public void pageUpdated(JasperPrint jasperPrint, int pageIndex) {
                        // NOP
                    }
                });
            }

            // adding this listener so that error results in the exception being thrown
            FillResultListener fillResult = new FillResultListener();
            fillHandle.addListener(fillResult);

            // set the cancelable
            FillHandleCancelable cancelable = new FillHandleCancelable(fillHandle);
            setExecutionCancelable(cancelable);
            try {
                // the fill will run on the same thread
                fillHandle.startFill();
            } finally {
                clearExecutionCancelable();
            }

            // set the result if not already set
            setResult(result, fillHandle.isPaginated(), true);

            // we don't need to do anything more here, AsyncJasperPrintAccessor gets the completed events
            if (log.isDebugEnabled()) {
                log.debug("Ended fill for request " + currentExecutionId());
            }
        }
    }

    protected static class SynchronousFillHandle extends BaseFillHandle {

        protected SynchronousFillHandle(JasperReportsContext jrContext, JasperReport jasperReport,
                Map<String, Object> parameters, JRDataSource dataSource) throws JRException {
            super(jrContext, jasperReport, parameters, dataSource, null);
        }

        @Override
        protected Executor getReportExecutor() {
            return SynchronousExecutor.INSTANCE;
        }

        protected boolean isPaginated() {
            JRFillContext fillContext = filler.getFillContext();
            return fillContext == null //conservatively considered paginated
                    || !fillContext.isIgnorePagination();
        }
    }

    protected static class SynchronousExecutor implements Executor {

        public static final SynchronousExecutor INSTANCE = new SynchronousExecutor();

        protected SynchronousExecutor() {
            //NOP
        }

        public void execute(Runnable command) {
            // run in the same thread
            command.run();
        }
    }

    protected abstract class ReportRunnable implements Runnable {
        protected final ReportUnitRequestBase request;
        protected final ReportFiller filler;
        private final ReportExecutionListener executionListener;

        public ReportRunnable(ReportUnitRequestBase request, ReportFiller filler,
                ReportExecutionListener executionListener) {
            this.request = request;
            this.filler = filler;
            this.executionListener = executionListener;
        }

        public final void run() {
            executionListener.start();
            try {
                incrementStartReportExecutionCount(request.isAsynchronous());
                startExecution(request);
                try {
                    runReport();
                } finally {
                    endExecution(request);
                    incrementEndReportExecutionCount(request.isAsynchronous());
                }
            } catch (Throwable t) {
                filler.setError(t);
                incrementErrorReportExecutionCount();
            } finally {
                long size = -1;
                if (recordSizeof) {
                    try {
                        size = getSizer().sizeOf(filler.getResult(), null, null).getCalculated();
                    } catch (Throwable e) {
                    }
                    if (log.isDebugEnabled()) {
                        log.debug("report result in memory size is " + size);
                    }
                }
                executionListener.end(!filler.hasError(), size);
            }
        }

        SizeOfEngine getSizer() {
            return new DefaultSizeOfEngine(1000, false);
        }

        protected abstract void runReport() throws Exception;
    }

    protected class ReportFill extends ReportRunnable {
        private final ExecutionContext context;
        private final ReportUnit reportUnit;
        private final boolean inMemoryUnit;

        public ReportFill(ExecutionContext context, ReportUnitRequestBase request, ReportUnit reportUnit,
                boolean inMemoryUnit, ReportFiller filler, ReportExecutionListener executionListener) {
            super(request, filler, executionListener);
            this.context = context;
            this.reportUnit = reportUnit;
            this.inMemoryUnit = inMemoryUnit;
        }

        protected void runReport() {
            if (log.isDebugEnabled()) {
                log.debug("Initiating report execution for " + reportUnit.getURIString());
            }

            if (request.getReportContext() != null) {
                request.getReportContext()
                        .setParameterValue(ReportUnitRequestBase.REPORT_CONTEXT_PARAMETER_REPORT_UNIT, reportUnit);
            }

            // if we got here that means we have read perms for the report,
            // but access to other resources should only require execute perms
            ExecutionContext runtimeContext = getRuntimeExecutionContext(context);
            RepositoryContextHandle repositoryContextHandle = setThreadRepositoryContext(runtimeContext, reportUnit,
                    reportUnit.getURIString());

            TimeZoneContextHolder.setTimeZone(context == null ? TimeZone.getDefault() : context.getTimeZone());
            try {
                // if we got here that means we have read perms for the report,
                // but access to other resources should only require execute perms
                Map unitResources = loadFinalResources(runtimeContext, reportUnit.getResources());
                OrigContextClassLoader origContext = setContextClassLoader(runtimeContext, unitResources,
                        inMemoryUnit, repositoryContextHandle);
                try {
                    JasperReport report = getJasperReport(runtimeContext, request, reportUnit, inMemoryUnit);

                    // check if the report inhibits async viewing
                    boolean reportAsyncable = JRPropertiesUtil
                            .getInstance(DefaultJasperReportsContext.getInstance())
                            .getBooleanProperty(report, AsynchronousFillHandle.PROPERTY_REPORT_ASYNC, true);
                    filler.setAsyncable(reportAsyncable);

                    // parameter values used to match data snapshots
                    Map<String, Object> dataParameters = getDataParameters(runtimeContext, request, reportUnit,
                            report);

                    boolean hasCachedData = false;
                    if (dataCacheProvider != null) {
                        DataCacheSnapshot snapshot = dataCacheProvider.setReportExecutionCache(runtimeContext,
                                request, reportUnit, dataParameters);
                        if (snapshot != null) {
                            hasCachedData = true;
                            filler.setDataTimestamp(snapshot.getMetadata().getSnapshotDate());
                        }
                    }

                    @SuppressWarnings("unchecked")
                    Map<String, Object> reportParameters = getReportParameters(runtimeContext, report,
                            request.getReportParameters());
                    setReportTemplates(runtimeContext, unitResources, reportParameters);

                    // put additional data parameters into the report parameters map for data source services
                    for (Entry<String, Object> entry : dataParameters.entrySet()) {
                        if (!reportParameters.containsKey(entry.getKey())) {
                            reportParameters.put(entry.getKey(), entry.getValue());
                        }
                    }

                    // if we have cached data, run without data source and query
                    if (hasCachedData) {
                        runWithCachedData(runtimeContext, report, dataParameters, reportParameters);
                    }

                    // if we don't have a result yet (no cache or invalid cache), run with data source and query
                    if (!filler.hasResult()) {
                        runWithDataSource(runtimeContext, report, reportParameters);
                    }
                } finally {
                    revert(origContext);
                }
            } finally {
                TimeZoneContextHolder.setTimeZone(null);
                resetThreadRepositoryContext(repositoryContextHandle);
            }
        }

        protected void runWithCachedData(ExecutionContext runtimeContext, JasperReport report,
                Map<String, Object> dataParameters, Map<String, Object> reportParameters) {
            if (log.isDebugEnabled()) {
                log.debug("running report with cached data");
            }

            try {
                // run the report (note that it would run on the same thread even when async)
                Map<String, Object> paramsCopy = new HashMap<String, Object>(reportParameters);
                fillReport(runtimeContext, reportUnit, report, paramsCopy, null, null, filler);
                // success
            } catch (RuntimeException e) {
                // if a result has already been submitted, it's too late to do something
                if (filler.hasResult()) {
                    throw e;
                }

                // if we have a data snapshot exception, rerun the report with the original data source.
                // otherwise rethrow the exception
                int snapshotExcIdx = ExceptionUtils.indexOfType(e, DataSnapshotException.class);
                if (snapshotExcIdx < 0) {
                    throw e;
                }

                if (log.isDebugEnabled()) {
                    log.debug("running report " + reportUnit.getURIString()
                            + " with the data snapshot resulted in exception", e);
                }

                // remove the populated data snapshot and continue
                dataCacheProvider.handleInvalidSnapshot(runtimeContext, request, reportUnit, dataParameters);
            }
        }

        protected void runWithDataSource(ExecutionContext runtimeContext, JasperReport report,
                Map<String, Object> reportParameters) {
            // setting the default data timestamp
            filler.setDataTimestamp(new Date());

            ReportDataSource datasource = null;
            ResourceReference queryRef = reportUnit.getQuery();
            Query query = queryRef == null ? null : getFinalResource(runtimeContext, queryRef, Query.class);
            if (query != null && query.getDataSource() != null) {
                datasource = (ReportDataSource) getFinalResource(runtimeContext, query.getDataSource(),
                        Resource.class);
            }

            ResourceReference dsRef = reportUnit.getDataSource();
            if (datasource == null && dsRef != null) {
                datasource = (ReportDataSource) getFinalResource(runtimeContext, dsRef, Resource.class);
            }

            if (request.getReportContext() != null) {
                request.getReportContext().setParameterValue(
                        ReportUnitRequestBase.REPORT_CONTEXT_PARAMETER_REPORT_UNIT_DATA_SOURCE, datasource);
            }

            if (log.isDebugEnabled()) {
                log.debug("running report with data source");
            }

            fillReport(runtimeContext, reportUnit, report, reportParameters, datasource, query, filler);
        }
    }

    protected static interface ReportExecutionCancelable {
        boolean cancel(String requestId);
    }

    protected static class QueryExecuterCancelable implements ReportExecutionCancelable {
        private final JRQueryExecuter queryExecuter;

        public QueryExecuterCancelable(JRQueryExecuter queryExecuter) {
            this.queryExecuter = queryExecuter;
        }

        public boolean cancel(String requestId) {
            if (log.isDebugEnabled()) {
                log.debug("Canceling query executer for request " + requestId);
            }

            try {
                return queryExecuter.cancelQuery();
            } catch (JRException e) {
                if (log.isWarnEnabled()) {
                    log.warn("Error canceling query executer", e);
                }

                return false;
            }
        }
    }

    protected static class FillHandleCancelable implements ReportExecutionCancelable {
        private final FillHandle fillHandle;

        public FillHandleCancelable(FillHandle fillHandle) {
            this.fillHandle = fillHandle;
        }

        public boolean cancel(String requestId) {
            if (log.isDebugEnabled()) {
                log.debug("Canceling filler for request " + requestId);
            }

            try {
                fillHandle.cancellFill();
            } catch (JRException e) {
                if (log.isWarnEnabled()) {
                    log.warn("Error canceling report filler", e);
                }

                return false;
            }
            return true;
        }
    }

    @XmlRootElement(name = "reportExecution")
    public static class ReportExecutionStatus implements ReportExecutionStatusInformation {

        public static String PROPERTY_REPORTURI = "REPORTEXECUTIONSTATUS_REPORTURI";
        public static String PROPERTY_JOBID = "REPORTEXECUTIONSTATUS_JOBID";
        public static String PROPERTY_JOBLABEL = "REPORTEXECUTIONSTATUS_JOBLABEL";
        public static String PROPERTY_FIRETIME = "REPORTEXECUTIONSTATUS_FIRETIME";
        public static String PROPERTY_USERNAME = "REPORTEXECUTIONSTATUS_USERNAME";
        public static String PROPERTY_QUARTZJOB = "REPORTEXECUTIONSTATUS_QUARTZJOB";

        private String requestId;
        private String reportURI;
        private User owner;
        private boolean cancelRequested;
        private Date startTime;
        private ReportExecutionCancelable cancelable;
        private Map<String, Object> propertyMap = null;

        public ReportExecutionStatus() {
            /* empty constructor is required for JAXB */}

        public ReportExecutionStatus(Request request) {
            this.requestId = request.getId();
            this.startTime = new Date();
        }

        public ReportExecutionStatus(Request request, Map<String, Object> propertyMap) {
            this.requestId = request.getId();
            this.propertyMap = propertyMap;
            this.startTime = new Date();
            if (propertyMap == null)
                propertyMap = new HashMap<String, Object>();
        }

        @XmlTransient
        public User getOwner() {
            return owner;
        }

        public void setOwner(User owner) {
            this.owner = owner;
        }

        @XmlElement
        public String getRequestId() {
            return requestId;
        }

        public void setRequestId(String requestId) {
            this.requestId = requestId;
        }

        public synchronized void setCancelable(ReportExecutionCancelable cancelable) {
            this.cancelable = cancelable;
        }

        @XmlTransient
        public synchronized ReportExecutionCancelable getCancelable() {
            return cancelable;
        }

        public synchronized boolean cancel() {
            this.cancelRequested = true;
            if (propertyMap != null) {
                Object quartzJob = propertyMap.get(PROPERTY_QUARTZJOB);
                if ((quartzJob != null) && (quartzJob instanceof ReportExecutionJob)) {
                    ((ReportExecutionJob) quartzJob).cancelExecution();
                }
            }
            synchronized (this) {
                if (cancelable != null) {
                    return cancelable.cancel(requestId);
                }
                if (log.isDebugEnabled()) {
                    log.debug("No cancelable found for request " + requestId);
                }
                return true;
            }
        }

        @XmlTransient
        public Map<String, Object> getProperties() {
            return propertyMap;
        }

        @XmlTransient
        public synchronized boolean isCancelRequested() {
            return this.cancelRequested;
        }

        public String getReportURI() {
            return reportURI;
        }

        public void setReportURI(String reportURI) {
            this.reportURI = reportURI;
        }

        @XmlTransient
        public Date getStartTime() {
            return this.startTime;
        }
    }

    protected class FillResultListener implements AsynchronousFilllListener {

        private JasperPrint jasperPrint;
        private boolean paginated;

        public void reportFinished(JasperPrint jasperPrint) {
            this.jasperPrint = jasperPrint;
        }

        public void reportCancelled() {
            throw new JSException("Report has been cancelled");
        }

        public void reportFillError(Throwable t) {
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            }

            throw new JSException("Error filling report", t);
        }

        public JasperPrint getJasperPrint() {
            return jasperPrint;
        }

        public boolean isPaginated() {
            return paginated;
        }

        public void setPaginated(boolean paginated) {
            this.paginated = paginated;
        }
    }

    protected void checkRequestCanceled() {
        ReportExecutionStatus status = currentExecutionStatus();
        if (status != null && status.isCancelRequested()) {
            throw new JSException("Report execution request canceled");
        }
    }

    protected void setExecutionCancelable(ReportExecutionCancelable cancelable) {
        ReportExecutionStatus status = currentExecutionStatus();
        if (status == null) {
            if (log.isDebugEnabled()) {
                log.debug("Execution not tracked");
            }
        } else {
            status.setCancelable(cancelable);
        }
    }

    protected void clearExecutionCancelable() {
        setExecutionCancelable(null);
    }

    public boolean cancelExecution(String requestId) {
        ReportExecutionStatus status = engineExecutions.get(requestId);
        if (status == null) {
            if (log.isDebugEnabled()) {
                log.debug("No execution found for request " + requestId);
            }

            return false;
        }
        synchronized (status) {
            return status.cancel();
        }
    }

    protected Map<String, Object> getDataParameters(ExecutionContext runtimeContext, ReportUnitRequestBase request,
            ReportUnit reportUnit, JasperReport report) {
        Map<String, Object> dataParameters = new LinkedHashMap<String, Object>();
        for (ReportDataParameterContributor contributor : getDataParameterContributors()) {
            Map<String, Object> providerParameters = contributor.getDataParameters(runtimeContext, request,
                    reportUnit, report);
            if (providerParameters != null && !providerParameters.isEmpty()) {
                dataParameters.putAll(providerParameters);
            }
        }
        return dataParameters;
    }

    protected Map getReportParameters(ExecutionContext context, JasperReport report, Map requestParameters) {
        Map reportParameters = new HashMap();

        reportParameters.put(JRParameter.REPORT_URL_HANDLER_FACTORY, RepositoryURLHandlerFactory.getInstance());

        if (context != null && context.getLocale() != null
                && reportParameters.get(JRParameter.REPORT_LOCALE) == null) {
            reportParameters.put(JRParameter.REPORT_LOCALE, context.getLocale());
        }

        if (context != null && context.getTimeZone() != null) {
            reportParameters.put(JRParameter.REPORT_TIME_ZONE, context.getTimeZone());
        }

        if (requestParameters != null) {
            reportParameters.putAll(requestParameters);
        }

        setBuiltinParameters(context, true, report.getParameters(), reportParameters, null);

        return reportParameters;
    }

    protected void setReportTemplates(ExecutionContext context, Map unitResources, Map reportParameters) {
        if (!reportParameters.containsKey(JRParameter.REPORT_TEMPLATES)) {
            List templates = new ArrayList();
            for (Iterator it = unitResources.values().iterator(); it.hasNext();) {
                FileResource resource = (FileResource) it.next();
                if (resource.getFileType().equals(FileResource.TYPE_STYLE_TEMPLATE)) {
                    JRTemplate template = loadTemplate(context, resource);
                    templates.add(template);
                }
            }
            reportParameters.put(JRParameter.REPORT_TEMPLATES, templates);
        }
    }

    protected JRTemplate loadTemplate(ExecutionContext context, FileResource resource) {
        InputStream templateDataStream = getFileResourceDataStream(context, resource);
        try {
            return JRXmlTemplateLoader.load(templateDataStream);
        } catch (JRRuntimeException e) {
            throw new JSExceptionWrapper(e);
        }
    }

    protected void revert(OrigContextClassLoader origContext) {
        if (origContext.set) {

            Thread.currentThread().setContextClassLoader(origContext.origClassLoader);

            for (Iterator it = origContext.jars.iterator(); it.hasNext();) {
                CacheObject cacheJarFile = (CacheObject) it.next();
                if (!cacheJarFile.isCached()) {
                    JarFile jarFile = (JarFile) cacheJarFile.getObject();
                    dispose(jarFile);
                }
            }
        }
    }

    protected static class OrigContextClassLoader {
        public final boolean set;
        public final ClassLoader origClassLoader;
        public final List jars;

        public static final OrigContextClassLoader NOT_SET = new OrigContextClassLoader(false);

        private OrigContextClassLoader(boolean set) {
            this.set = set;
            this.origClassLoader = null;
            this.jars = null;
        }

        public OrigContextClassLoader(ClassLoader origClassLoader, List jars) {
            this.set = true;
            this.origClassLoader = origClassLoader;
            this.jars = jars;
        }
    }

    protected OrigContextClassLoader setContextClassLoader(ExecutionContext context, ResourceContainer reportUnit,
            boolean inMemoryUnit) {
        return setContextClassLoader(context, reportUnit, inMemoryUnit, null);
    }

    protected OrigContextClassLoader setContextClassLoader(ExecutionContext context, ResourceContainer reportUnit,
            boolean inMemoryUnit, RepositoryContextHandle repositoryContextHandle) {
        Map unitResources = loadFinalResources(context, reportUnit.getResources());
        return setContextClassLoader(context, unitResources, inMemoryUnit, repositoryContextHandle);
    }

    protected Map loadFinalResources(ExecutionContext context, List<ResourceReference> resources) {
        Map finalResources = new LinkedHashMap();
        if (resources != null) {
            for (ResourceReference resRef : resources) {
                FileResource resource = getFinalFileResource(context, resRef);
                finalResources.put(resRef, resource);
            }
        }

        return finalResources;
    }

    protected OrigContextClassLoader setContextClassLoader(ExecutionContext context, Map unitResources,
            boolean inMemoryUnit, RepositoryContextHandle repositoryContextHandle) {
        Thread thread = Thread.currentThread();
        ClassLoader origClassLoader = thread.getContextClassLoader();
        ClassLoader jarsClassLoader;
        ClassLoader newClassLoader = null;

        List jarFiles = getJarFiles(context, unitResources, !inMemoryUnit);
        if (jarFiles.isEmpty()) {
            jarsClassLoader = origClassLoader;
        } else {
            newClassLoader = jarsClassLoader = getJarsClassLoader(origClassLoader, jarFiles);
        }

        RepositoryContext repositoryContext = repositoryContextHandle == null ? null
                : repositoryContextHandle.getRepositoryContext();
        if (repositoryContext != null || RepositoryUtil.hasThreadRepositoryContext()) {
            //use the repository context for the keys? not required for now as for now the context is always freshly set on the thread.
            Map resourceBundleKeys = getResourceBundleKeys(context, unitResources);
            if (!resourceBundleKeys.isEmpty()) {
                newClassLoader = getResourcesClassLoader(jarsClassLoader, resourceBundleKeys, inMemoryUnit,
                        repositoryContext);
            }
        }

        OrigContextClassLoader origContext;
        if (newClassLoader == null) {
            origContext = OrigContextClassLoader.NOT_SET;
        } else {
            origContext = new OrigContextClassLoader(origClassLoader, jarFiles);
            thread.setContextClassLoader(newClassLoader);
        }

        return origContext;
    }

    protected ClassLoader getJarsClassLoader(ClassLoader origClassLoader, List jarFiles) {
        boolean caching = true;
        for (Iterator it = jarFiles.iterator(); caching && it.hasNext();) {
            CacheObject cacheJarFile = (CacheObject) it.next();
            caching &= cacheJarFile.isCached();
        }

        ClassLoader classLoader;
        if (caching) {
            Map childrenClassLoaders;
            synchronized (jarsClassLoaderCache) {
                childrenClassLoaders = (Map) jarsClassLoaderCache.get(origClassLoader);
                if (childrenClassLoaders == null) {
                    childrenClassLoaders = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);
                    jarsClassLoaderCache.put(origClassLoader, childrenClassLoaders);
                }
            }
            Object classLoaderKey = getJarFileNames(jarFiles);
            synchronized (childrenClassLoaders) {
                classLoader = (ClassLoader) childrenClassLoaders.get(classLoaderKey);
                if (classLoader == null) {
                    if (log.isDebugEnabled()) {
                        log.debug("Creating class loader for parent " + origClassLoader + " and jars "
                                + classLoaderKey);
                    }
                    classLoader = createJarsClassLoader(origClassLoader, jarFiles);
                    childrenClassLoaders.put(classLoaderKey, classLoader);
                }
            }
        } else {
            classLoader = createJarsClassLoader(origClassLoader, jarFiles);
        }
        return classLoader;
    }

    protected ClassLoader createJarsClassLoader(ClassLoader origClassLoader, List jarFiles) {
        JarFile[] jars = new JarFile[jarFiles.size()];
        int i = 0;
        for (Iterator it = jarFiles.iterator(); it.hasNext(); ++i) {
            jars[i] = (JarFile) ((CacheObject) it.next()).getObject();
        }

        return new JarsClassLoader(jars, origClassLoader, reportJarsProtectionDomainProvider.getProtectionDomain());
    }

    private Object getJarFileNames(List jarFiles) {
        List jarFileNames = new ArrayList(jarFiles.size());
        for (Iterator it = jarFiles.iterator(); it.hasNext();) {
            JarFile jar = (JarFile) ((CacheObject) it.next()).getObject();
            jarFileNames.add(jar.getName());
        }
        return jarFileNames;
    }

    protected ClassLoader getResourcesClassLoader(ClassLoader parent, Map resourceBundleKeys, boolean inMemoryUnit,
            RepositoryContext repositoryContext) {
        ClassLoader repositoryResourceClassLoader;
        if (inMemoryUnit) {
            repositoryResourceClassLoader = new RepositoryResourceClassLoader(parent, resourceBundleKeys, true,
                    repositoryContext);
        } else {
            Map childrenClassLoaders;
            synchronized (resourcesClassLoaderCache) {
                childrenClassLoaders = (Map) resourcesClassLoaderCache.get(parent);
                if (childrenClassLoaders == null) {
                    childrenClassLoaders = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);
                    resourcesClassLoaderCache.put(parent, childrenClassLoaders);
                }
            }
            synchronized (childrenClassLoaders) {
                //put something from the repository context in the key?
                repositoryResourceClassLoader = (ClassLoader) childrenClassLoaders.get(resourceBundleKeys);
                if (repositoryResourceClassLoader == null) {
                    if (log.isDebugEnabled()) {
                        log.debug("Creating class loader for parent " + parent + " and resources "
                                + resourceBundleKeys);
                    }
                    repositoryResourceClassLoader = new RepositoryResourceClassLoader(parent, resourceBundleKeys,
                            false, repositoryContext);
                    childrenClassLoaders.put(resourceBundleKeys, repositoryResourceClassLoader);
                }
            }
        }
        return repositoryResourceClassLoader;
    }

    protected List getJarFiles(ExecutionContext context, Map unitResources, boolean cache) {
        List jarFiles = new ArrayList();
        for (Iterator it = unitResources.values().iterator(); it.hasNext();) {
            FileResource resource = (FileResource) it.next();
            if (resource.getFileType().equals(FileResource.TYPE_JAR)) {
                CacheObject cacheJarFile = getCacheJarFile(context, resource, cache);
                jarFiles.add(cacheJarFile);
            }
        }
        return jarFiles;
    }

    protected Map getResourceBundleKeys(ExecutionContext context, Map unitResources) {
        Map resourceBundleKeys = new HashMap();
        for (Iterator it = unitResources.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            ResourceReference resRef = (ResourceReference) entry.getKey();
            FileResource finalResource = (FileResource) entry.getValue();
            if (finalResource.getFileType().equals(FileResource.TYPE_RESOURCE_BUNDLE)) {
                String resName;
                if (resRef.isLocal()) {
                    resName = resRef.getLocalResource().getName();
                } else {
                    resName = finalResource.getName();//TODO first reference name?
                }

                String resPathKey = repositoryContextManager.getRepositoryPathKey(finalResource.getURIString());
                String uri = repositoryContextManager.getRepositoryUriForKey(resPathKey);
                RepositoryResourceKey resourceKey = new RepositoryResourceKey(resPathKey, uri,
                        finalResource.getVersion(), finalResource.getCreationDate());
                resourceBundleKeys.put(resName, resourceKey);
            }
        }
        return resourceBundleKeys;
    }

    protected JasperReport getJasperReport(ExecutionContext context, ReportUnitRequestBase request,
            ReportUnit reportUnit, boolean inMemoryUnit) {
        JasperDesignCache cache = JasperDesignCache.getInstance(DefaultJasperReportsContext.getInstance(),
                request.getReportContext());
        return getJasperReport(context, cache, reportUnit, inMemoryUnit);
    }

    protected JasperReport getJasperReport(ExecutionContext context, JasperDesignCache cache, ReportUnit reportUnit,
            boolean inMemoryUnit) {
        FileResource reportRes = getFinalResource(context, reportUnit.getMainReport(), FileResource.class);
        String location = reportUnit.getPath();
        return getJasperReport(context, cache, reportRes, location, inMemoryUnit);
    }

    protected JasperReport getJasperReport(ExecutionContext context, JasperDesignCache cache,
            FileResource reportRes, String location, boolean inMemoryUnit) {
        JasperReport report = null;
        try {
            if (inMemoryUnit) {
                InputStream fileResourceData = getFileResourceDataStream(context, reportRes);
                report = compileReport(fileResourceData);
            } else {
                //TODO use an in-memory cache of compiled reports

                if (cache != null) {
                    report = cache.getJasperReport(location);
                }

                if (report == null) {
                    InputStream compiledReport = getCompiledReport(context, reportRes);
                    try {
                        report = (JasperReport) JRLoader.loadObject(compiledReport);

                        if (cache != null) {
                            cache.set(location, report);
                        }

                    } catch (JRException e) {
                        Throwable cause = e.getCause();
                        if (cause == null || !(cause instanceof InvalidClassException)) {
                            throw e;
                        }

                        if (log.isInfoEnabled()) {
                            log.info(
                                    "InvalidClassException caught while loading compiled report, clearing the compiled report cache");
                        }
                        clearCompiledReportCache();

                        //recompiling the report
                        compiledReport = getCompiledReport(context, reportRes);
                        report = (JasperReport) JRLoader.loadObject(compiledReport);
                    }
                }

            }
            return report;
        } catch (JRException e) {
            log.error(e, e);
            throw new JSExceptionWrapper(e);
        }
    }

    protected JasperReport getJasperReport(ExecutionContext context, ReportUnit reportUnit, boolean inMemoryUnit) {
        FileResource reportRes = getFinalResource(context, reportUnit.getMainReport(), FileResource.class);
        JasperReport report;
        try {
            if (inMemoryUnit) {
                InputStream fileResourceData = getFileResourceDataStream(context, reportRes);
                report = compileReport(fileResourceData);
            } else {
                //TODO use an in-memory cache of compiled reports
                InputStream compiledReport = getCompiledReport(context, reportRes);
                try {
                    report = (JasperReport) JRLoader.loadObject(compiledReport);
                } catch (JRException e) {
                    Throwable cause = e.getCause();
                    if (cause == null || !(cause instanceof InvalidClassException)) {
                        throw e;
                    }

                    if (log.isInfoEnabled()) {
                        log.info(
                                "InvalidClassException caught while loading compiled report, clearing the compiled report cache");
                    }
                    clearCompiledReportCache();

                    //recompiling the report
                    compiledReport = getCompiledReport(context, reportRes);
                    report = (JasperReport) JRLoader.loadObject(compiledReport);
                }
            }
            return report;
        } catch (JRException e) {
            log.error(e, e);
            throw new JSExceptionWrapper(e);
        }
    }

    protected void clearCompiledReportCache() {
        compiledReportsCache.clearCache(cacheableCompiledReports);
    }

    protected void addReportUnitToAuditEvent(final ReportUnit reportUnit) {
        getAuditContext().doInAuditContext("runReport", new AuditContext.AuditContextCallbackWithEvent() {
            public void execute(AuditEvent auditEvent) {
                auditEvent.setResourceUri(reportUnit.getURI());
                auditContext.setResourceTypeToAuditEvent(reportUnit.getResourceType(), auditEvent);
                String uri = reportUnit.getDataSource() != null ? reportUnit.getDataSource().getReferenceURI() : "";
                getAuditContext().addPropertyToAuditEvent("dataSource", uri, auditEvent);
            }
        });
    }

    protected void addPropertyToAuditEvent(final String propertyType, final Object param) {
        getAuditContext().doInAuditContext("runReport", new AuditContext.AuditContextCallbackWithEvent() {
            public void execute(AuditEvent auditEvent) {
                getAuditContext().addPropertyToAuditEvent(propertyType, param, auditEvent);
            }
        });
    }

    protected void fillReport(ExecutionContext context, ReportUnit reportUnit, JasperReport report,
            Map reportParameters, ReportDataSource datasource, Query query, ReportFiller filler) {
        ReportDataSourceService dataSourceService = null;
        boolean dsClosing = false;
        addReportUnitToAuditEvent(reportUnit);
        RepositoryContextHandle repositoryContextHandle = null;
        String originalTopicDataSource = getOriginalDataAdapterTopicUri(datasource);
        if (originalTopicDataSource != null)
            repositoryContextHandle = setThreadRepositoryContext(context, reportUnit, originalTopicDataSource);
        try {
            if (datasource != null) {
                dataSourceService = createDataSourceService(datasource);
                dataSourceService.setReportParameterValues(reportParameters);
            }

            long renderingStartTime = System.currentTimeMillis();
            addPropertyToAuditEvent("reportRenderingStartTime", new Date(renderingStartTime));
            if (query == null) {
                filler.fillReport(report, reportParameters, null);
            } else {
                fillQueryReport(context, report, reportParameters, query, filler);
            }
            addPropertyToAuditEvent("reportRenderingTime", System.currentTimeMillis() - renderingStartTime);

            dsClosing = true;
            if (dataSourceService != null) {
                dataSourceService.closeConnection();
                dataSourceService = null;
            }
        } catch (JRException e) {
            log.error("Error while filling report", e);
            throw new JSExceptionWrapper(e);
        } finally {
            resetThreadRepositoryContext(repositoryContextHandle);
            if (!dsClosing && dataSourceService != null) {
                //only exception cases
                try {
                    dataSourceService.closeConnection();
                } catch (Exception e) {
                    log.error("Error while closing data source connection", e);
                }
            }
        }
    }

    protected JRDataSource createQueryDataSource(JRQueryExecuter queryExecuter) throws JRException {
        checkRequestCanceled();

        if (log.isDebugEnabled()) {
            log.debug("Executing query for request " + currentExecutionId());
        }

        QueryExecuterCancelable cancelable = new QueryExecuterCancelable(queryExecuter);
        setExecutionCancelable(cancelable);
        try {
            return queryExecuter.createDatasource();
        } finally {
            clearExecutionCancelable();

            if (log.isDebugEnabled()) {
                log.debug("Ended query for request " + currentExecutionId());
            }
        }
    }

    protected JasperPrint fillReport(JasperReport report, Map parameters, JRDataSource dataSource)
            throws JRException {
        FillResultListener result = fillReport(getEffectiveJasperReportsContext(), report, parameters, dataSource);
        return result.getJasperPrint();
    }

    protected FillResultListener fillReport(JasperReportsContext jasperReportsContext, JasperReport report,
            Map parameters, JRDataSource dataSource) throws JRException {
        FillResultListener fillResult = new FillResultListener();
        checkRequestCanceled();

        if (log.isDebugEnabled()) {
            log.debug("Filling report for request " + currentExecutionId());
        }

        SynchronousFillHandle fillHandle = new SynchronousFillHandle(jasperReportsContext, report, parameters,
                dataSource);
        // add the listener that will store the result
        fillHandle.addListener(fillResult);

        // set the cancelable
        FillHandleCancelable cancelable = new FillHandleCancelable(fillHandle);
        setExecutionCancelable(cancelable);
        try {
            // run the fill
            fillHandle.startFill();
        } finally {
            clearExecutionCancelable();
        }

        if (log.isDebugEnabled()) {
            log.debug("Ended fill for request " + currentExecutionId());
        }

        // the fill runs on the same thread, so we can set the following and return the result
        fillResult.setPaginated(fillHandle.isPaginated());
        return fillResult;
    }

    private String getOriginalDataAdapterTopicUri(ReportDataSource datasource) {
        if (datasource instanceof DataView)
            return ((DataView) datasource).getOriginalDataAdapterTopic();
        return null;
    }

    protected void fillQueryReport(ExecutionContext context, JasperReport report, Map reportParameters, Query query,
            ReportFiller filler) throws JRException {
        JRQueryExecuter queryExecuter = JRQueryExecuterAdapter.createQueryExecuter(report, reportParameters, query);
        boolean closing = false;
        try {
            JRDataSource reportDatasource = createQueryDataSource(queryExecuter);
            filler.fillReport(report, reportParameters, reportDatasource);

            closing = true;
            queryExecuter.close();
        } finally {
            if (!closing) {
                queryExecuter.close();
            }
        }
    }

    public ReportDataSourceService createDataSourceService(ReportDataSource dataSource) {
        ReportDataSourceServiceFactory factory = (ReportDataSourceServiceFactory) getDataSourceServiceFactories()
                .getBean(dataSource.getClass());
        return factory.createService(dataSource);
    }

    /**
    *
    */
    public Resource[] getResources(ResourceReference jrxmlReference) {
        //TODO context?
        FileResource jrxml = getFinalResource(null, jrxmlReference, FileResource.class);
        return ResourceCollector.getResources(getFileResourceDataStream(null, jrxml));
    }

    protected <T extends Resource> T getRepositoryResource(ExecutionContext context, String uri, Class<T> type) {
        return (T) getRepositoryService().getResource(context, uri, type);
    }

    /**
    * get an execution context containing a PermissionOverride which lets you access execute-only resources
    * when you are running a report
    * @return
    */
    public ExecutionContext getRuntimeExecutionContext() {
        return getRuntimeExecutionContext(null);
    }

    public static ExecutionContext getRuntimeExecutionContext(ExecutionContext originalContext) {
        return ExecutionContextImpl.getRuntimeExecutionContext(originalContext);
    }

    public <T extends Resource> T getFinalResource(ExecutionContext context, ResourceReference res, Class<T> type) {
        T finalRes;
        if (res == null) {
            return null;
        }
        if (res.isLocal()) {
            finalRes = (T) res.getLocalResource();
        } else {
            finalRes = getRepositoryResource(context, res.getReferenceURI(), type);
        }
        return finalRes;
    }

    protected FileResource getFinalFileResource(ExecutionContext context, ResourceReference resRef) {
        ExecutionContext runtimeContext = getRuntimeExecutionContext(context);
        FileResource res = getFinalResource(runtimeContext, resRef, FileResource.class);
        while (res.isReference()) {
            res = getRepositoryResource(runtimeContext, res.getReferenceURI(), FileResource.class);
        }
        return res;
    }

    public ValidationResult validate(ExecutionContext context, ReportUnit reportUnit) {
        OrigContextClassLoader origContext = setContextClassLoader(context, reportUnit, true);
        ValidationResultImpl result = new ValidationResultImpl();
        try {
            ResourceReference mainReport = reportUnit.getMainReport();
            if (mainReport != null) {
                validateJRXML(context, result, mainReport);
            }

            List resources = reportUnit.getResources();
            if (resources != null && !resources.isEmpty()) {
                for (Iterator iter = resources.iterator(); iter.hasNext();) {
                    ResourceReference resource = (ResourceReference) iter.next();
                    validateJRXML(context, result, resource);
                }
            }
        } finally {
            revert(origContext);
        }
        return result;
    }

    protected void validateJRXML(ExecutionContext context, ValidationResultImpl result,
            ResourceReference resourceRef) {
        FileResource resource = getFinalFileResource(context, resourceRef);
        if (resource.getFileType().equals(FileResource.TYPE_JRXML)) {
            try {
                JasperCompileManager.compileReport(getFileResourceDataStream(context, resource));
            } catch (JRException e) {
                ValidationDetailImpl detail = new ValidationDetailImpl();
                detail.setValidationClass(FileResource.class);
                detail.setName(resource.getName());
                detail.setLabel(resource.getLabel());
                detail.setResult(ValidationResult.STATE_ERROR);
                detail.setException(e);
                detail.setMessage(e.getMessage());
                result.addValidationDetail(detail);
            }
        }
    }

    public ReportUnitResult executeReportUnitRequest(ExecutionContext context, ReportUnitRequest request) {
        ReportUnit reportUnit = getRepositoryResource(context, request.getReportUnitUri(), ReportUnit.class);
        return fillReport(context, request, reportUnit, false);
    }

    public ReportUnitResult executeTrialReportUnitRequest(ExecutionContext context,
            TrialReportUnitRequest request) {
        return fillReport(context, request, request.getReportUnit(), true);
    }

    public InputStream getCompiledReport(ExecutionContext context, InputStream jrxmlData) {
        JasperReport report = compileReport(jrxmlData);
        byte[] reportBytes = reportBytes(report);
        return new ByteArrayInputStream(reportBytes);
    }

    protected JasperReport compileReport(InputStream jrxmlData) {
        JasperDesign design = loadReportDesign(jrxmlData);
        JasperReport report = compileReport(design);
        return report;
    }

    protected JasperDesign loadReportDesign(InputStream jrxmlData) {
        JasperDesign design;
        try {
            design = JRXmlLoader.load(jrxmlData);
        } catch (JRException e) {
            log.error("error loading JRXML", e);
            throw new JSExceptionWrapper(e);
        }
        return design;
    }

    protected JasperReport compileReport(JasperDesign design) {
        if (log.isDebugEnabled()) {
            log.debug("compiling report " + design.getName());
        }

        try {
            JasperReport report = JasperCompileManager.compileReport(design);
            return report;
        } catch (JRException e) {
            log.error("error compiling report", e);
            throw new JSExceptionWrapper(e);
        }
    }

    protected InputStream getCompiledReport(ExecutionContext context, FileResource jrxml) {
        return compiledReportsCache.cache(context, jrxml, cacheableCompiledReports);
    }

    protected byte[] reportBytes(JasperReport report) {
        try {
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            JRSaver.saveObject(report, bout);
            byte[] reportBytes = bout.toByteArray();
            return reportBytes;
        } catch (JRException e) {
            log.error(e, e);
            throw new JSExceptionWrapper(e);
        }
    }

    public InputStream getCompiledReport(ExecutionContext context, String jrxmlURI) {
        return compiledReportsCache.cache(context, jrxmlURI, cacheableCompiledReports);
    }

    private void addReportUnitTypeToAudit(final Resource reportUnit) {
        auditContext.doInAuditContext("runReport", new AuditContext.AuditContextCallbackWithEvent() {
            public void execute(AuditEvent auditEvent) {
                if (reportUnit != null) {
                    if (auditEvent.getResourceType() == null) {
                        auditContext.setResourceTypeToAuditEvent(reportUnit.getResourceType(), auditEvent);
                    }
                    if (auditEvent.getResourceUri() == null) {
                        auditEvent.setResourceUri(reportUnit.getURI());
                    }
                }
            }
        });
    }

    public JasperReport getMainJasperReport(ExecutionContext context, String reportUnitURI) {
        JasperReport jasperReport = null;
        ReportUnit reportUnit = getRepositoryResource(context, reportUnitURI, ReportUnit.class);
        if (reportUnit != null) {
            addReportUnitTypeToAudit(reportUnit);
            OrigContextClassLoader origContext = setContextClassLoader(context, reportUnit, false);
            try {
                jasperReport = getJasperReport(context, reportUnit, false);
            } finally {
                revert(origContext);
            }
        }
        return jasperReport;
    }

    @Override
    public JasperReport getMainJasperReport(ExecutionContext context, InputControlsContainer container) {
        JasperReport jasperReport = null;
        if (container != null) {
            if (container instanceof ReportUnit) {
                addReportUnitTypeToAudit(container);
                OrigContextClassLoader origContext = setContextClassLoader(context, (ReportUnit) container, false);
                try {
                    jasperReport = getJasperReport(context, (ReportUnit) container, false);
                } finally {
                    revert(origContext);
                }
            } else {
                throw new JSException("Unsupported report object " + container.getClass().getName());
            }
        }

        return jasperReport;
    }

    public void release() {
        tempJarFiles.release();
    }

    protected void dispose(JarFile jarFile) {
        try {
            jarFile.close();
        } catch (IOException e) {
            log.warn("Unable to close jar file \"" + jarFile.getName() + "\"", e);
        }
        File file = new File(jarFile.getName());
        if (file.exists() && !file.delete()) {
            log.warn("Unable to delete jar file \"" + jarFile.getName() + "\"");
        }
    }

    public void clearCaches(Class resourceItf, String resourceURI) {
        if (FileResource.class.isAssignableFrom(resourceItf)) {
            //TODO check JRXML type
            compiledReportsCache.clearCache(resourceURI, cacheableCompiledReports);

            tempJarFiles.remove(resourceURI);
        }
    }

    public SecurityContextProvider getSecurityContextProvider() {
        return securityContextProvider;
    }

    public void setSecurityContextProvider(SecurityContextProvider securityContextProvider) {
        this.securityContextProvider = securityContextProvider;
    }

    public OrderedMap executeQuery(ExecutionContext context, ResourceReference queryReference, String keyColumn,
            String[] resultColumns, ResourceReference defaultDataSourceReference) {
        return executeQuery(context, queryReference, keyColumn, resultColumns, defaultDataSourceReference, null);
    }

    protected void addReportUnitToAuditEvent(final String dataSourceUri) {
        getAuditContext().doInAuditContext("inputControlsQuery", new AuditContext.AuditContextCallbackWithEvent() {
            public void execute(AuditEvent auditEvent) {
                getAuditContext().addPropertyToAuditEvent("dataSource", dataSourceUri, auditEvent);
            }
        });
    }

    protected ReportDataSource getQueryDataSource(ExecutionContext context, ResourceReference dataSourceReference,
            ResourceReference defaultDataSourceReference) {

        if (dataSourceReference == null) {
            dataSourceReference = defaultDataSourceReference;
        }

        addReportUnitToAuditEvent(dataSourceReference != null ? dataSourceReference.getTargetURI() : null);

        return (ReportDataSource) getFinalResource(context, dataSourceReference, Resource.class);
    }

    public OrderedMap executeQuery(ExecutionContext context, ResourceReference queryReference, String keyColumn,
            String[] resultColumns, ResourceReference defaultDataSourceReference, Map parameterValues) {
        return executeQuery(context, queryReference, keyColumn, resultColumns, defaultDataSourceReference,
                parameterValues, null, true);
    }

    public OrderedMap executeQuery(ExecutionContext context, ResourceReference queryReference, String keyColumn,
            String[] resultColumns, ResourceReference defaultDataSourceReference, Map parameterValues,
            Map<String, Class<?>> parameterTypes, boolean formatValueColumns) {
        // set exec-only context
        context = getRuntimeExecutionContext(context);

        Query query = getFinalResource(context, queryReference, Query.class);

        ReportDataSource dataSource = getQueryDataSource(context, query.getDataSource(),
                defaultDataSourceReference);

        ReportDataSourceService dataSourceService = createDataSourceService(dataSource);
        boolean dsClosing = false;
        try {
            Map parameters = new HashMap();

            if (parameterValues != null) {
                parameters.putAll(parameterValues);
            }

            List additionalParameters = new ArrayList();
            setBuiltinParameters(context, false, null, parameters, additionalParameters);

            dataSourceService.setReportParameterValues(parameters);
            if (queryManipulator != null) {
                String originalSQL = query.getSql();
                String newSQL;
                if (queryManipulator instanceof IDataSourceAwareQueryManipulator) {
                    newSQL = ((IDataSourceAwareQueryManipulator) queryManipulator).updateQuery(originalSQL,
                            parameters, dataSource);
                } else {
                    newSQL = queryManipulator.updateQuery(query.getSql(), parameters);
                }
                query.setSql(newSQL);
            }
            long start = System.currentTimeMillis();
            OrderedMap result = JRQueryExecuterAdapter.executeQuery(query, keyColumn, resultColumns, parameters,
                    parameterTypes, additionalParameters, formatValueColumns);
            if (valueQueryLog.isDebugEnabled()) {
                valueQueryLog
                        .debug("query took " + (System.currentTimeMillis() - start) + " ms: " + query.getSql());
                valueQueryLog.debug("params: " + parameters);
            }
            dsClosing = true;
            if (dataSourceService != null) {
                dataSourceService.closeConnection();
                dataSourceService = null;
            }

            return result;
        } finally {
            if (!dsClosing && dataSourceService != null) {
                //only exception cases
                try {
                    dataSourceService.closeConnection();
                } catch (Exception e) {
                    log.error("Error while closing data source connection", e);
                }
            }
        }

    }

    public void setBuiltinParameters(ExecutionContext context, boolean onlyExistingParameters,
            JRParameter[] existingJRParameters, Map parametersMap, List additionalParameters) {
        for (Object o : getBuiltInParameterProviders()) {
            BuiltInParameterProvider pProvider = (BuiltInParameterProvider) o;

            // set standard parameters that will always be available

            List<Object[]> results = pProvider.getParameters(context, additionalParameters, parametersMap);

            for (Object[] aResult : results) {
                setBuiltInParameter(context, onlyExistingParameters, existingJRParameters, parametersMap,
                        additionalParameters, aResult);
            }

            // add parameters that are built in and requested
            // This is only needed when we are running a report.
            // Parameters in stand alone queries (engineService.executeQuery) are managed by FilterCore (cascading engine)
            if (onlyExistingParameters) {
                for (JRParameter jrParameter : existingJRParameters) {
                    Object parameterMapValue = parametersMap.get(jrParameter.getName());
                    if (parameterMapValue == null) {
                        Object[] aResult = pProvider.getParameter(context, additionalParameters, parametersMap,
                                jrParameter.getName());
                        if (aResult != null) {
                            setBuiltInParameter(context, onlyExistingParameters, existingJRParameters,
                                    parametersMap, additionalParameters, aResult);
                        }
                    }
                }
            }
        }
    }

    public void setBuiltInParameter(ExecutionContext context, boolean onlyExistingParameters,
            JRParameter[] existingJRParameters, Map parametersMap, List additionalParameters, Object[] aResult) {

        JRParameter jrParameter = (JRParameter) aResult[0];
        Object aValue = aResult[1];
        boolean set = false;

        Object parameterFromMap = parametersMap.get(jrParameter.getName());

        JRParameter existingJRParameter = null;

        if (onlyExistingParameters) {
            for (JRParameter existingJRParameterFromArray : existingJRParameters) {
                if (existingJRParameterFromArray.getName().equals(jrParameter.getName())) {
                    existingJRParameter = existingJRParameterFromArray;
                    break;
                }
            }
        }

        if (onlyExistingParameters) {
            if (existingJRParameter == null) {
                // don't add: leave set = false
                /* The default value expression has lower priority than user defined value. Bug #32316
                            } else if (existingJRParameter.getDefaultValueExpression() != null) {
                                //do not set if parameter has default value expression
                                if (log.isDebugEnabled()) {
                log.debug("Report parameter " + jrParameter.getName() + " has a default value expression, not setting value");
                                }
                */
            } else if (!existingJRParameter.getValueClass().isAssignableFrom(jrParameter.getValueClass())) {
                //do not set if parameter has type incompatible with parameter
                if (log.isDebugEnabled()) {
                    log.debug(
                            "Report parameter " + jrParameter.getName() + " type " + jrParameter.getValueClassName()
                                    + " not compatible with java.lang.String, not setting value");
                }
            } else {
                //set the value
                set = true;
            }
        } else if (parameterFromMap == null) {
            //set the value anyway, it will be accessible through the parametersMap map
            set = true;
        }

        if (set) {
            if (log.isDebugEnabled()) {
                log.debug("Setting report parameter " + jrParameter.getName() + " to " + aValue);
            }

            if (!onlyExistingParameters) {
                additionalParameters.add(jrParameter);
            }

            parametersMap.put(jrParameter.getName(), aValue);
        }

    }

    public ResourceLookup[] getDataSources(ExecutionContext context, String queryLanguage) {
        ResourceLookup[] datasources;
        if (queryLanguage == null) {
            datasources = repository.findResource(context, FilterCriteria.createFilter(ReportDataSource.class));
        } else {
            Set dataSourceTypes = dataSourceServiceFactories.getSupportingDataSourceTypes(queryLanguage);
            if (dataSourceTypes == null || dataSourceTypes.isEmpty()) {
                datasources = null;
            } else {
                FilterCriteria[] criteria = new FilterCriteria[dataSourceTypes.size()];
                int i = 0;
                for (Iterator it = dataSourceTypes.iterator(); it.hasNext(); ++i) {
                    Class type = (Class) it.next();
                    criteria[i] = FilterCriteria.createFilter(type);
                }
                datasources = repository.findResources(context, criteria);
            }
        }
        return datasources;
    }

    public String getQueryLanguage(ExecutionContext context, ResourceReference jrxmlResource) {
        JasperDesign jasperDesign = loadJRXML(context, jrxmlResource);
        JRQuery query = jasperDesign.getQuery();
        return query == null ? null : query.getLanguage();
    }

    protected JasperDesign loadJRXML(ExecutionContext context, ResourceReference jrxmlResource) {
        JasperDesign jasperDesign;
        FileResource jrxmlRes = getFinalFileResource(context, jrxmlResource);
        InputStream jrxmlData = getFileResourceDataStream(context, jrxmlRes);
        boolean close = true;
        try {
            jasperDesign = JRXmlLoader.load(jrxmlData);

            close = false;
            jrxmlData.close();
        } catch (JRException e) {
            log.error("Error parsing JRXML", e);
            throw new JSExceptionWrapper(e);
        } catch (IOException e) {
            log.error(e, e);
            throw new JSExceptionWrapper(e);
        } finally {
            if (close) {
                try {
                    jrxmlData.close();
                } catch (IOException e) {
                    log.error(e, e);
                }
            }
        }
        return jasperDesign;
    }

    public Set getDataSourceTypes(ExecutionContext context, String queryLanguage) {
        return dataSourceServiceFactories.getSupportingDataSourceTypes(queryLanguage);
    }

    /*
     * you should use the getReportInputControlsInformation method and get the values
     * from there: ReportInputControlsInformation#getDefaultValuesMap()
     */
    @Deprecated
    public Map getReportInputControlDefaultValues(ExecutionContext context, String reportURI,
            Map initialParameters) {
        ReportUnit reportUnit = getRepositoryResource(context, reportURI, ReportUnit.class);
        ReportInputControlsInformation infos = this.getReportInputControlsInformation(context, reportUnit,
                initialParameters);
        return infos.getDefaultValuesMap();
    }

    /*
     *
     */
    public ReportInputControlsInformation getReportInputControlsInformation(ExecutionContext context,
            String reportURI, Map initialParameters) {
        ReportUnit reportUnit = getRepositoryResource(context, reportURI, ReportUnit.class);

        return getReportInputControlsInformation(context, reportUnit, initialParameters);
    }

    protected void setSnapshotParameterValues(ExecutionContext context, ReportUnit reportUnit,
            ReportInputControlsInformation infos) {
        Set<String> controlNames = infos.getControlNames();
        if (controlNames.isEmpty()) {
            // nothing to do
            return;
        }

        DataSnapshotPersistentMetadata snapshot = dataCacheProvider.loadSavedDataSnapshot(context, reportUnit);
        if (snapshot == null) {
            return;
        }

        Map<String, Object> snapshotParams = snapshot.getSnapshotMetadata().getParameters();
        if (snapshotParams == null || snapshotParams.isEmpty()) {
            return;
        }

        if (log.isDebugEnabled()) {
            log.debug("setting default param values for " + reportUnit.getURIString() + " from data snapshot "
                    + reportUnit.getDataSnapshotId());
        }

        for (String controlName : controlNames) {
            ReportInputControlInformation info = infos.getInputControlInformation(controlName);
            if (snapshotParams.containsKey(controlName)) {
                Object value = snapshotParams.get(controlName);
                // check if it matches the expected type
                if (matchesInputControlType(info, value)) {
                    info.setDefaultValue(value);
                }
            }
        }
    }

    protected boolean matchesInputControlType(ReportInputControlInformation info, Object value) {
        // nulls are allowed
        if (value == null) {
            return true;
        }

        // check the base value type
        Class<?> controlType = info.getValueType();

        //TODO: need to correctly handle case when we have inputControl without JRParameter -
        //in this case valueType and nestedType will be null.
        if (controlType == null) {
            if (log.isDebugEnabled()) {
                log.debug(String.format("Could not determine type of snapshot parameter %s",
                        info.getParameterName()));
            }

            return false;
        }

        if (!controlType.isInstance(value)) {
            if (log.isDebugEnabled()) {
                log.debug("snapshot parameter " + info.getParameterName() + " of type " + value.getClass().getName()
                        + " does not match expected type " + controlType.getName());
            }

            return false;
        }

        if (Collection.class.isAssignableFrom(controlType)) {
            // for collection types, check the first value against the nested type
            Collection<?> collectionValues = (Collection<?>) value;
            Class nestedType = info.getNestedType();
            if (nestedType != null && !collectionValues.isEmpty()) {
                // we're assuming a homogeneous collection, only looking at first value
                Object firstValue = collectionValues.iterator().next();
                if (!nestedType.isInstance(firstValue)) {
                    if (log.isDebugEnabled()) {
                        log.debug("snapshot collection parameter " + info.getParameterName() + " with item type "
                                + firstValue.getClass().getName() + " does not match expected type "
                                + nestedType.getName());
                    }

                    return false;
                }
            }
        }

        return true;
    }

    /*
     *
     */
    @Override
    public ReportInputControlsInformation getReportInputControlsInformation(ExecutionContext context,
            InputControlsContainer icContainer, Map initialParameters) {

        RepositoryContextHandle repositoryContextHandle = setThreadRepositoryContext(context,
                (ResourceContainer) icContainer, icContainer.getURIString());
        try {
            OrigContextClassLoader origContext = setContextClassLoader(context, (ResourceContainer) icContainer,
                    false, repositoryContextHandle);

            try {

                List<ResourceReference> inputControls = icContainer.getInputControls();
                MessageSource messages;

                JasperReport report;
                if (icContainer instanceof ReportUnit) {
                    report = getJasperReport(context, (ReportUnit) icContainer, false);
                    messages = MessageSourceLoader.loadMessageSource(context, (ResourceContainer) icContainer,
                            repository);
                } else {
                    throw new JRException("Unsupported report object " + icContainer.getClass().getName());
                }

                ReportInputControlsInformation infos = getReportInputControlsInformation(context, report,
                        inputControls, initialParameters, messages);

                if (icContainer instanceof ReportUnit) {
                    // use parameters values from the data snapshot if any
                    setSnapshotParameterValues(context, (ReportUnit) icContainer, infos);
                }

                return infos;
            } catch (JRException e) {
                throw new JSExceptionWrapper(e);
            } finally {
                revert(origContext);
            }
        } finally {
            resetThreadRepositoryContext(repositoryContextHandle);
        }
    }

    /* 
     * 
     */
    protected ReportInputControlsInformation getReportInputControlsInformation(ExecutionContext context,
            JasperReport report, List<ResourceReference> inputControls, Map initialParameters,
            MessageSource serverMessageSource) throws JRException {
        ReportInputControlsInformationImpl result = new ReportInputControlsInformationImpl();
        if (inputControls == null) {
            return result;
        }

        // add Locale, Timezone and Built-In Parameters
        initialParameters = getReportParameters(context, report, initialParameters);

        // collect the JRParameter array to a Map
        Map<String, JRParameter> jrParametersMap = new HashMap();
        JRParameter[] params = report.getParameters();
        for (int i = 0; i < params.length; i++) {
            jrParametersMap.put(params[i].getName(), params[i]);
        }

        ResourceBundle reportBundle = loadResourceBundle(report);

        // evaluate the default values from Jasper Report
        Map jrDefaultValues = JRParameterDefaultValuesEvaluator.evaluateParameterDefaultValues(report,
                initialParameters);

        // build the JasperReportInputControlInformation object for given input controls
        for (ResourceReference inputControlRef : inputControls) {
            // ExecutionContext may contain no attribute.  If so, add "execute override" attribute before calling getFinalResource()
            InputControl inputControl = getFinalResource(getRuntimeExecutionContext(context), inputControlRef,
                    InputControl.class);

            String name = inputControl.getName();
            JRParameter param = jrParametersMap.get(name);

            // try to get i18n label for input control
            String label = InputControlLabelResolver.resolve(inputControl.getLabel(), reportBundle,
                    serverMessageSource);

            // try to get i18n labels for List Of Values input controls options
            ReportInputControlValuesInformation valuesInformation = null;
            ResourceReference listOfValuesResourceReference = inputControl.getListOfValues();
            if (listOfValuesResourceReference != null && (serverMessageSource != null || reportBundle != null)) {
                valuesInformation = getReportInputControlValuesInformation(context, reportBundle,
                        serverMessageSource, listOfValuesResourceReference);
            }

            ReportInputControlInformation info;

            if (param != null) {
                JasperReportInputControlInformation jrInfo = new JasperReportInputControlInformation();
                jrInfo.setReportParameter(param);
                jrInfo.setDefaultValue(jrDefaultValues.get(name));
                jrInfo.setPromptLabel(label);
                jrInfo.setReportInputControlValuesInformation(valuesInformation);
                info = jrInfo;
            } else {
                // input control is not assigned to any report parameter.
                // Create an anonymous ReportInputControlInformation class which doesn't need the JRParameter.
                info = createReportInputControlInformationWithoutParameter(inputControl.getName(), label,
                        valuesInformation);
            }
            result.setInputControlInformation(name, info);
        }

        return result;
    }

    private ReportInputControlInformation createReportInputControlInformationWithoutParameter(final String name,
            final String label, final ReportInputControlValuesInformation valuesInformation) {
        return new ReportInputControlWithoutParameterInformation(name, label, valuesInformation);
    }

    private ReportInputControlValuesInformation getReportInputControlValuesInformation(ExecutionContext context,
            ResourceBundle resourceBundle, MessageSource messageSource,
            ResourceReference listOfValuesResourceReference) {

        ListOfValues listOfValuesResource = null;
        if (listOfValuesResourceReference.isLocal()) {
            listOfValuesResource = (ListOfValues) listOfValuesResourceReference.getLocalResource();
        } else {
            listOfValuesResource = (ListOfValues) repository.getResource(context,
                    listOfValuesResourceReference.getReferenceURI());
        }

        return ReportInputControlValuesInformationLoader
                .getReportInputControlValuesInformation(listOfValuesResource, resourceBundle, messageSource);
    }

    protected ResourceBundle loadResourceBundle(JasperReport report) {
        ResourceBundle bundle = null;
        String baseName = report.getResourceBundle();
        if (baseName != null) {
            Locale locale = LocaleContextHolder.getLocale();
            bundle = JRResourcesUtil.loadResourceBundle(baseName, locale);
        }
        return bundle;
    }

    public String getReportParameterLabelKeyPrefix() {
        return reportParameterLabelKeyPrefix;
    }

    public void setReportParameterLabelKeyPrefix(String reportParameterLabelKeyPrefix) {
        this.reportParameterLabelKeyPrefix = reportParameterLabelKeyPrefix;
    }

    public byte[] compileReport(ExecutionContext context, FileResource resource) {
        if (log.isDebugEnabled()) {
            log.debug("compiling report " + resource.getURIString());
        }

        InputStream jrxmlData = getFileResourceDataStream(context, resource);
        JasperDesign design = loadReportDesign(jrxmlData);
        JasperReport report = compileReport(design);
        byte[] reportBytes = reportBytes(report);

        boolean updateJRXML = false;
        if (!design.hasUUID()) {
            //FIXME refactor this into a JRXMLFixer
            if (log.isDebugEnabled()) {
                log.debug("JRXML at " + resource.getURIString() + " does not have UUID");
            }

            if (isAutoUpdateJRXMLs()) {
                updateJRXML = true;
            }
        }

        if (jrxmlFixerList != null) {
            for (JRXMLFixer fixer : jrxmlFixerList) {
                if (fixer.fix(design)) {
                    if (log.isDebugEnabled()) {
                        log.debug("JRXML at " + resource.getURIString() + " updated by " + fixer);
                    }

                    updateJRXML = true;
                }
            }
        }

        if (updateJRXML) {
            if (log.isDebugEnabled()) {
                log.debug("Auto updating JRXML resource at " + resource.getURIString());
            }

            autoUpdateJRXMLResource(resource, design);
        }

        return reportBytes;
    }

    protected void autoUpdateJRXMLResource(FileResource resource, JasperDesign report) {
        try {
            MemoryDataContainer jrxmlData = new MemoryDataContainer();
            OutputStream jrxmlStream = jrxmlData.getOutputStream();
            JRXmlWriter.writeReport(report, jrxmlStream, "UTF-8");//always writing UTF-8
            jrxmlStream.close();

            // skipping object security
            getUnsecureRepository().replaceFileResourceData(resource.getURIString(), jrxmlData);

            // also updating in the resource if it includes the original data
            if (resource.hasData()) {
                resource.setData(jrxmlData.getData());
            }
        } catch (Exception e) {
            // only log the error
            log.warn("Failed to auto update JRXML resource with missing UUID at " + resource.getURIString(), e);
        }
    }

    public Executor getSyncReportExecutorService() {
        return syncReportExecutorService;
    }

    public void setSyncReportExecutorService(Executor syncReportExecutorService) {
        this.syncReportExecutorService = syncReportExecutorService;
    }

    public Executor getAsyncReportExecutorService() {
        return asyncReportExecutorService;
    }

    public void setAsyncReportExecutorService(Executor asyncReportExecutorService) {
        this.asyncReportExecutorService = asyncReportExecutorService;
    }

    public List<ReportExecutionStatusInformation> getReportExecutionStatusList() {
        return new ArrayList<ReportExecutionStatusInformation>(engineExecutions.values());
    }

    public List<ReportExecutionStatusInformation> getReportExecutionStatusList(
            ReportExecutionStatusSearchCriteria searchCriteria) {
        List<ReportExecutionStatusInformation> executions = getReportExecutionStatusList();
        if (executions == null)
            return null;
        List<ReportExecutionStatusInformation> newSet = new ArrayList<ReportExecutionStatusInformation>();
        for (ReportExecutionStatusInformation entry : executions) {
            Map<String, Object> entryProperties = entry.getProperties();
            if ((searchCriteria.getReportURI() != null) && !searchCriteria.getReportURI().equalsIgnoreCase(
                    (String) entryProperties.get(EngineServiceImpl.ReportExecutionStatus.PROPERTY_REPORTURI)))
                continue;
            newSet.add(entry);
        }
        return newSet;
    }

    public ReportExecutionStatusInformation getReportExecutionStatusByID(String reportExecutionStatusID) {
        return engineExecutions.get(reportExecutionStatusID);
    }

    public List<ReportExecutionStatusInformation> getSchedulerReportExecutionStatusList() {
        List<ReportExecutionStatusInformation> executions = getReportExecutionStatusList();
        if (executions == null)
            return null;
        List<ReportExecutionStatusInformation> newSet = new ArrayList<ReportExecutionStatusInformation>();
        for (ReportExecutionStatusInformation entry : executions) {
            Map<String, Object> entryProperties = entry.getProperties();
            if ((entryProperties != null) && (entryProperties.size() > 0))
                newSet.add(entry);
        }
        return newSet;
    }

    public List<ReportExecutionStatusInformation> getSchedulerReportExecutionStatusList(
            SchedulerReportExecutionStatusSearchCriteria searchCriteria) {
        List<ReportExecutionStatusInformation> executions = getReportExecutionStatusList();
        if (executions == null)
            return null;
        List<ReportExecutionStatusInformation> newSet = new ArrayList<ReportExecutionStatusInformation>();
        for (ReportExecutionStatusInformation entry : executions) {
            Map<String, Object> entryProperties = entry.getProperties();
            if ((searchCriteria.getJobID() != null) && !equals(searchCriteria.getJobID(),
                    entryProperties.get(EngineServiceImpl.ReportExecutionStatus.PROPERTY_JOBID)))
                continue;
            if ((searchCriteria.getJobLabel() != null) && !equals(searchCriteria.getJobLabel(),
                    entryProperties.get(EngineServiceImpl.ReportExecutionStatus.PROPERTY_JOBLABEL)))
                continue;
            if ((searchCriteria.getReportURI() != null) && !equals(searchCriteria.getReportURI(),
                    entryProperties.get(EngineServiceImpl.ReportExecutionStatus.PROPERTY_REPORTURI)))
                continue;
            if ((searchCriteria.getFireTimeFrom() != null || searchCriteria.getFireTimeTo() != null)
                    && !isBetween((Date) entryProperties.get(ReportExecutionStatus.PROPERTY_FIRETIME),
                            searchCriteria.getFireTimeFrom(), searchCriteria.getFireTimeTo()))
                continue;
            if ((searchCriteria.getUserName() != null) && !equals(searchCriteria.getUserName(),
                    entryProperties.get(EngineServiceImpl.ReportExecutionStatus.PROPERTY_USERNAME)))
                continue;
            newSet.add(entry);
        }
        return newSet;
    }

    /**
     * Compares if report execution fire time is in given range (inclusive). From and to time are both nullable, so following cases are processed:
     * from != null && to != null - true if fire time in range (inclusive)
     * from == null && to != null - fire time should be before or equals to "to"
     * from != null && to == null - fire time should be after or equals to "from"
     * from == null && to == null - result is always false
     *
     * @param fireTime - fire time
     * @param from - search criteria from time, nullable
     * @param to - search criteria to time, nullable
     * @return true if fire time is in range (inclusive)
     */
    private boolean isBetween(Date fireTime, Date from, Date to) {
        boolean result = false;
        if (fireTime != null && (from != null || to != null)) {
            if (from != null && to != null)
                result = (fireTime.after(from) || fireTime.equals(from))
                        && (fireTime.before(to) || fireTime.equals(to));
            else if (from != null && to == null)
                result = fireTime.after(from) || fireTime.equals(from);
            else if (from == null && to != null)
                result = fireTime.before(to) || fireTime.equals(to);
        }
        return result;
    }

    private boolean equals(Object obj1, Object obj2) {
        if (obj1 == obj2)
            return true;
        if ((obj1 == null) || (obj2 == null))
            return false;
        if ((obj1 instanceof Date) && (obj2 instanceof Date)) {
            Calendar calendar1 = new GregorianCalendar();
            calendar1.setTime((Date) obj1);
            calendar1.set(Calendar.SECOND, 0);
            calendar1.set(Calendar.MILLISECOND, 0);
            Calendar calendar2 = new GregorianCalendar();
            calendar2.setTime((Date) obj2);
            calendar2.set(Calendar.SECOND, 0);
            calendar2.set(Calendar.MILLISECOND, 0);
            return (calendar1.compareTo(calendar2) == 0);
        } else if ((obj1 instanceof String) && (obj2 instanceof String)) {
            return ((String) obj1).equalsIgnoreCase((String) obj2);
        } else
            return obj1.equals(obj2);
    }

    public DataCacheProvider getDataCacheProvider() {
        return dataCacheProvider;
    }

    public void setDataCacheProvider(DataCacheProvider dataCacheProvider) {
        this.dataCacheProvider = dataCacheProvider;
    }

    public boolean isAutoUpdateJRXMLs() {
        return autoUpdateJRXMLs;
    }

    public void setAutoUpdateJRXMLs(boolean autoUpdateJRXMLs) {
        this.autoUpdateJRXMLs = autoUpdateJRXMLs;
    }

    public RepositoryUnsecure getUnsecureRepository() {
        return unsecureRepository;
    }

    public void setUnsecureRepository(RepositoryUnsecure unsecureRepository) {
        this.unsecureRepository = unsecureRepository;
    }

    public List<ReportExecutionListenerFactory> getReportExecutionListenerFactories() {
        return reportExecutionListenerFactories;
    }

    public void setReportExecutionListenerFactories(
            List<ReportExecutionListenerFactory> reportExecutionListenerFactories) {
        this.reportExecutionListenerFactories = reportExecutionListenerFactories;
    }

    public JasperReportsContext getJasperReportsContext() {
        return jasperReportsContext;
    }

    public void setJasperReportsContext(JasperReportsContext jasperReportsContext) {
        this.jasperReportsContext = jasperReportsContext;
    }

    private void incrementStartReportExecutionCount(Boolean asynchronous) {
        if (asynchronous) {
            this.startAsyncReportExecutionCount.incrementAndGet();
        } else
            this.startSyncReportExecutionCount.incrementAndGet();
    }

    private void incrementEndReportExecutionCount(Boolean asynchronous) {
        if (asynchronous) {
            this.endAsyncReportExecutionCount.incrementAndGet();
        } else
            this.endSyncReportExecutionCount.incrementAndGet();
    }

    private void incrementErrorReportExecutionCount() {
        this.errorReportExecutionCount.incrementAndGet();
    }

    public List<ReportDataParameterContributor> getDataParameterContributors() {
        return dataParameterContributors;
    }

    public void setDataParameterContributors(List<ReportDataParameterContributor> dataParameterContributors) {
        this.dataParameterContributors = dataParameterContributors;
    }

    public boolean isRecordSizeof() {
        return recordSizeof;
    }

    public void setRecordSizeof(boolean recordSizeof) {
        this.recordSizeof = recordSizeof;
    }

    public List<JRXMLFixer> getJrxmlFixerList() {
        return jrxmlFixerList;
    }

    public void setJrxmlFixerList(List<JRXMLFixer> jrxmlFixerList) {
        this.jrxmlFixerList = jrxmlFixerList;
    }

}