org.sakaiproject.reports.logic.impl.ReportsManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.reports.logic.impl.ReportsManagerImpl.java

Source

/**********************************************************************************
 * $URL:https://source.sakaiproject.org/svn/osp/trunk/reports/api-impl/src/java/org/theospi/portfolio/reports/model/impl/ReportsManagerImpl.java $
 * $Id:ReportsManagerImpl.java 9134 2006-05-08 20:28:42Z chmaurer@iupui.edu $
 ***********************************************************************************
 *
 * Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **********************************************************************************/
package org.sakaiproject.reports.logic.impl;

import org.apache.commons.codec.digest.DigestUtils;
import org.hibernate.HibernateException;
import org.hibernate.Transaction;
import org.jdom.CDATA;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import org.jdom.transform.JDOMResult;
import org.jdom.transform.JDOMSource;
import org.quartz.*;
import org.sakaiproject.api.app.scheduler.JobBeanWrapper;
import org.sakaiproject.api.app.scheduler.SchedulerManager;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.authz.cover.FunctionManager;
import org.sakaiproject.component.app.scheduler.jobs.SpringJobBeanWrapper;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.content.api.ContentCollection;
import org.sakaiproject.content.api.ContentCollectionEdit;
import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.content.api.ContentResource;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.entity.api.ResourcePropertiesEdit;
import org.sakaiproject.event.cover.NotificationService;
import org.sakaiproject.exception.*;
import org.sakaiproject.metaobj.security.AuthenticationManager;
import org.sakaiproject.metaobj.security.AuthorizationFacade;
import org.sakaiproject.metaobj.security.AuthorizationFailedException;
import org.sakaiproject.metaobj.security.Authorization;
import org.sakaiproject.metaobj.security.model.AuthZMap;
import org.sakaiproject.metaobj.shared.mgt.IdManager;
import org.sakaiproject.metaobj.shared.model.Agent;
import org.sakaiproject.metaobj.shared.model.Id;
import org.sakaiproject.metaobj.shared.model.IdImpl;
import org.sakaiproject.metaobj.shared.model.MimeType;
import org.sakaiproject.metaobj.worksite.mgt.WorksiteManager;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.api.ToolSession;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.tool.cover.ToolManager;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.sakaiproject.metaobj.security.impl.AllowAllSecurityAdvisor;
import org.sakaiproject.reports.service.ParameterResultsPostProcessor;
import org.sakaiproject.reports.service.ReportExecutionException;
import org.sakaiproject.reports.service.ReportFunctions;
import org.sakaiproject.reports.service.ReportsManager;
import org.sakaiproject.reports.service.ResultProcessor;
import org.sakaiproject.reports.model.*;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.net.MalformedURLException;
import java.net.URL;

import org.sakaiproject.time.api.Time;
import org.sakaiproject.util.Validator;

import java.lang.reflect.Method;

/**
 * This class is a singleton that manages the reports on a general basis
 * <p/>
 * <p/>
 * When getting the reports a user can run this class checks the
 * "sakai.reports.useWarehouse" sakai.properties property.  0 is no reports. 1 is
 * the warehouse reports. 2 is live data reports.  and 3 is both warehouse and
 * live data reports.  The default is 1.  The default report has a setting of
 * operating on the warehouse.
 * <p/>
 * The dataSource is for the data warehouse data source.  If it is set then that
 * source is used.  If it is not set then the code tries to load in the data warehouse
 * dataSource.  It does this because that is the default dw dataSource.  The
 * data warehouse is not deployed then the dw dataSource won't exist.  If it is referenced
 * in the components.xml then there would be errors at startup.  Thus we don't reference
 * it there and programmatically pull it.  This way it could be null (when dw is not
 * deployed) and then the dataSource falls back to the sakai dataSource.
 * <p/>
 * the sakai.properties property "sakai.reports.forceColumnLabelUppercase" is used to standardize
 * the column label.  MySQL will keep the column titles exactly as specified in the query.
 * Oracle on the other hand seems to make all the column labels uppercase.  This makes writing
 * a query and an xsl that works in both databases more difficult.  This defaults to 1 (otherwise know as true)
 * Set this property to 0 and the column titles will pass through like the old behavior
 *
 * @author andersjb
 */
public class ReportsManagerImpl extends HibernateDaoSupport implements ReportsManager, BeanFactoryAware {
    /**
     * Enebles logging
     */

    private AuthenticationManager authnManager;
    /**
     * The global list of reports
     */
    private List reportDefinitions;

    /**
     * Class for converting a Id string to an Id class
     */
    private IdManager idManager = null;

    /**
     * the sakai class that manages permissions
     */
    private AuthorizationFacade authzManager;

    /**
     * The class that generates the database connection.  it is the data warehouse data source
     */
    private DataSource dataSource = null;

    /**
     * The class that generates the database connection.  it is the sakai data source
     */
    private DataSource sakaiDataSource = null;

    /**
     * an internal variable for whether or not the database connection should be closed after its use
     */
    private boolean canCloseConnection = true;

    /**
     * used to hash a reference so the hash isn't straight from the reference
     */
    private String secretKey = "sakai_reports";

    /**
     * used to allow artifacts to be downloaded (through adding an advisor)
     */
    private SecurityService securityService;

    private JobBeanWrapper jobBeanWrapper = null;
    /**
     * This is used to standardize the case of the column labels. This is helpful because
     * MySQL uses the same case as determined by the query.  Oracle makes them all uppercase.
     * This makes it easier to write the xsl in a database agnostic way
     */
    private Boolean forceColumnLabelUppercase;

    private SchedulerManager schedulerManager;

    private boolean autoDdl = true;

    private boolean upgrade24 = false;

    protected BeanFactory beanFactory;

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    /**
     * convert between the user formatted date and the database formatted date
     */
    private static SimpleDateFormat userDateFormat = new SimpleDateFormat("MM/dd/yyyy");
    private static SimpleDateFormat dbDateFormat = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * Tells us if the global database reportDefinitions was loaded
     */
    private ContentHostingService contentHosting;

    public List definedDefintions;
    /**
     * the name of key in the session into which the result is saved into
     */
    private static final String CURRENT_RESULTS_TAG = "org.sakaiproject.reports.service.ReportsManager.currentResults";

    /**
     * Called on after the startup of the singleton.  This sets the global
     * list of functions which will have permission managed by sakai
     *
     * @throws Exception
     */
    protected void init() throws Exception {
        logger.info("init() ReportsManagerImpl");
        // register functions
        FunctionManager.registerFunction(ReportFunctions.REPORT_FUNCTION_CREATE);
        FunctionManager.registerFunction(ReportFunctions.REPORT_FUNCTION_RUN);
        FunctionManager.registerFunction(ReportFunctions.REPORT_FUNCTION_VIEW);
        FunctionManager.registerFunction(ReportFunctions.REPORT_FUNCTION_EDIT);
        FunctionManager.registerFunction(ReportFunctions.REPORT_FUNCTION_DELETE);
        FunctionManager.registerFunction(ReportFunctions.REPORT_FUNCTION_SHARE);

        if (isUpgrade24()) {
            convert24to25Reports();
        }

    }

    public void clientInit() {
        if (isAutoDdl()) {
            initDefinedReportDefinitions();
        }
    }

    public AuthenticationManager getAuthnManager() {
        return authnManager;
    }

    public void setAuthnManager(AuthenticationManager authnManager) {
        this.authnManager = authnManager;
    }

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    public void setParentBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    /**
     * {@inheritDoc}
     */
    public void setReportDefinitions(List reportDefinitions) {
        List reportDefs = new ArrayList();
        Iterator iter = reportDefinitions.iterator();
        while (iter.hasNext()) {
            ReportDefinition rd = (ReportDefinition) iter.next();
            rd.finishLoading();
            reportDefs.add(rd);
        }
        this.reportDefinitions = reportDefs;
    }

    public List getReportDefinitions() {

        //load any reportDefinitions in the database
        List reportsDefs = loadReportsFromDB();
        setReportDefinitions(reportsDefs);
        return this.reportDefinitions;
    }

    public boolean isValidRole(String roleStr) {
        if (roleStr != null && roleStr.length() > 0) {
            String currentRole = getCurrentSite().getMember(SessionManager.getCurrentSessionUserId()).getRole()
                    .getId().toString();
            String[] roles = roleStr.split(",");
            for (int i = 0; i < roles.length; i++) {
                String role = roles[i];
                if (role.trim().equals(currentRole)) {
                    return true;
                }
            }
        } else {
            return true;
        }
        return false;

    }

    public boolean isValidWorksiteType(String typesStr) {
        if (typesStr != null && typesStr.length() > 0) {
            String[] types = typesStr.split(",");
            for (int i = 0; i < types.length; i++) {
                String type = types[i];
                if (type.trim().equals(getCurrentSiteType())) {
                    return true;
                } else if (type.trim().equals(getCurrentSite().getId())) {
                    return true;
                }
            }
        } else {
            return true;
        }
        return false;

    }

    /**
     * Given the param of whether or not the report is using the warehouse, should it be displayed is returned.
     * This works off the "sakai.reports.useWarehouse" sakai.properties property.  If there is no property
     * then we will only show the warehouse reports.
     * <p/>
     * If the input is null, then we automatically assume that it is using the warehouse (set to true).
     * <p/>
     * If the property is 0 then we don't show any report.  If bit 0 of the property is set then show
     * the data warehouse reports.  If bit 1 of the property is set then show the direct reports.  aka.
     * 0=no reports, 1= warehouse reports, 2= live data reports, 3= warehouse and live data reports
     *
     * @param usesWarehouse
     * @return true if report should be displayed
     */
    protected boolean hasWarehouseSetting(Boolean usesWarehouse) {
        int warehousePref = ServerConfigurationService.getInt("sakai.reports.useWarehouse", 1);

        if (warehousePref == 0) {
            return false;
        }

        if (usesWarehouse == null) {
            usesWarehouse = Boolean.TRUE;
        }

        // if bit 0 is set, show warehouse reports
        if ((warehousePref & 1) != 0 && usesWarehouse.booleanValue() == true) {
            return true;
        }

        if ((warehousePref & 2) != 0 && usesWarehouse.booleanValue() == false) {
            return true;
        }

        return false;
    }

    /**
     * This is the setter for the idManager
     */
    public void setIdManager(IdManager idManager) {
        this.idManager = idManager;
    }

    /**
     * This is the getter for the idManager
     *
     * @return IdManager
     */
    public IdManager getIdManager() {
        return idManager;
    }

    /**
     * This is the setter for the idManager
     */
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * {@inheritDoc}
     */
    public DataSource getDataSource() {
        configureDataSource();

        int warehousePref = ServerConfigurationService.getInt("sakai.reports.useWarehouse", 1);

        if (warehousePref == 1) {
            return dataSource;
        } else if (warehousePref == 2) {
            return sakaiDataSource;
        }

        throw new RuntimeException("Tried to get the report data source but the source was ambiguous.");
    }

    /**
     * {@inheritDoc}
     */
    public DataSource getDataSourceUseWarehouse(boolean useWarehouse) {
        configureDataSource();

        if (useWarehouse) {
            return dataSource;
        } else {
            return sakaiDataSource;
        }
    }

    /**
     * This function sets up the data warehouse data source.  If the dataSource exists then nothing changes.
     * Thus if the dataSource is set in the components.xml then it will use that for the data warehouse
     * data source.  Also if the dataSource has already been set up then this is skipped.
     * <p/>
     * So, if there is no dataSource set in the components.xml then we will default to the data warehouse
     * defined data source.  This is needed because the data warehouse may not be loaded and thus the dataSource
     * bean wouldn't be defined.  If we reference the dw dataSource when it doesn't exist then problems can
     * happen during startup.  Thus we load the dw dataSource dynamically.  If the dw dataSource doesn't
     * exist then we skip to the sakai dataSource.
     */
    private void configureDataSource() {
        if (dataSource == null) {
            dataSource = (DataSource) ComponentManager
                    .get("org.sakaiproject.warehouse.service.DataWarehouseManager.dataSource");
            if (dataSource == null) {
                dataSource = sakaiDataSource;
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public List getCurrentUserResults() {
        Session s = SessionManager.getCurrentSession();

        boolean runReports = can(ReportFunctions.REPORT_FUNCTION_RUN);
        boolean viewReports = can(ReportFunctions.REPORT_FUNCTION_VIEW);

        List returned = new ArrayList();

        if (viewReports | runReports) {
            List results = getHibernateTemplate().findByNamedQuery("findResultsByUser", s.getUserId());

            Iterator iter = results.iterator();
            while (iter.hasNext()) {
                ReportResult r = (ReportResult) iter.next();

                r.setIsSaved(true);
                r.setOwner(true);
            }
            returned.addAll(results);
        }

        if (runReports) {
            List liveReports = getHibernateTemplate().findByNamedQuery("findReportsByUser", s.getUserId());

            Iterator iter = liveReports.iterator();
            while (iter.hasNext()) {
                Report r = (Report) iter.next();

                r.getReportParams().size();

                r.connectToDefinition(getReportDefinitions());
                r.setIsSaved(true);
            }

            returned.addAll(liveReports);
        }

        return returned;
    }

    /**
     * Loads the global database reportDefinitions if they haven't been loaded yet
     * This is a stub.
     */
    private List loadReportsFromDB() {
        List<ReportDefinition> reportDefArray = new ArrayList<ReportDefinition>();
        List<ReportDefinitionXmlFile> reportDefs = getHibernateTemplate()
                .findByNamedQuery("findReportDefinitionFiles");

        for (ReportDefinitionXmlFile xmlFile : reportDefs) {
            ListableBeanFactory beanFactory = new XmlBeanFactory(new ByteArrayResource(xmlFile.getXmlFile()),
                    getBeanFactory());
            ReportDefinition repDef = getReportDefBean(beanFactory);
            repDef.finishLoading();
            repDef.setDbLoaded(true);
            if (isValidWorksiteType(repDef.getSiteType()) && isValidRole(repDef.getRole())
                    && hasWarehouseSetting(repDef.getUsesWarehouse())) {
                reportDefArray.add(repDef);
            }
        }
        Collections.sort(reportDefArray, new ReportDefinition.ReportDefinitionComparator());
        return reportDefArray;

    }

    /**
     * {@inheritDoc}
     */
    public ReportResult loadResult(ReportResult result) {
        ReportResult reportResult = (ReportResult) getHibernateTemplate().get(ReportResult.class,
                result.getResultId());

        //load the report too
        Report report = reportResult.getReport();

        String function = report.getIsLive() ? ReportFunctions.REPORT_FUNCTION_RUN
                : ReportFunctions.REPORT_FUNCTION_VIEW;

        getAuthzManager().checkPermission(function,
                getIdManager().getId(ToolManager.getCurrentPlacement().getId()));

        //set the report and report result to that of already been saved
        reportResult.setIsSaved(true);
        report.setIsSaved(true);

        //link the report deinition
        report.connectToDefinition(getReportDefinitions());

        reportResult.setReport(report);

        //give back the result
        return reportResult;
    }

    /**
     * {@inheritDoc}
     */
    public String getReportResultKey(ReportResult result, String ref) {
        String hashCode = DigestUtils.md5Hex(ref + getSecretKey());

        return hashCode;
    }

    /**
     * {@inheritDoc}
     */
    public void checkReportAccess(String id, String ref) {
        String hashCode = DigestUtils.md5Hex(ref + getSecretKey());

        if (!hashCode.equals(id)) {
            throw new AuthorizationFailedException();
        }

        getSecurityService().pushAdvisor(new AllowAllSecurityAdvisor());
    }

    public void setCurrentResult(ReportResult result) {
        ToolSession session = SessionManager.getCurrentToolSession();
        session.setAttribute(CURRENT_RESULTS_TAG, result);
    }

    /**
     * Pulls the ReportResults out of the session
     *
     * @return ReportResult
     */
    public ReportResult getCurrentResult() {
        ToolSession session = SessionManager.getCurrentToolSession();
        return (ReportResult) session.getAttribute(CURRENT_RESULTS_TAG);
    }

    /**
     * Given an id, this method finds and returns the ReportDefinition
     *
     * @param Id
     * @return ReportDefinition
     */
    public ReportDefinition findReportDefinition(String Id) {
        Iterator iter = getReportDefinitions().iterator();

        while (iter.hasNext()) {
            ReportDefinition rd = (ReportDefinition) iter.next();
            if (rd.getIdString().equals(Id)) {
                return rd;
            }
        }
        return null;
    }

    //   *************************************************************************
    //   *************************************************************************
    //         The process functions (non-getter/setter)

    /**
     * {@inheritDoc}
     */
    public void createReportParameters(Report report) {
        List reportDefParams = report.getReportDefinition().getReportDefinitionParams();
        ArrayList reportParams = new ArrayList(reportDefParams.size());

        Iterator iter = reportDefParams.iterator();

        while (iter.hasNext()) {
            ReportDefinitionParam rdp = (ReportDefinitionParam) iter.next();

            ReportParam rp = new ReportParam();

            rp.setReportDefinitionParam(rdp);
            rp.setReport(report);

            //   if the parameter is static then copy the value, otherwise it is filled by user
            if (rdp.getValueType().equals(ReportDefinitionParam.VALUE_TYPE_STATIC)) {
                rp.setValue(replaceSystemValues(rdp.getValue()));
            }
            reportParams.add(rp);
        }
        report.setReportParams(reportParams);
    }

    /**
     * Does a test to ensure that the parameters are valid
     * One can get to the parameter definitions through the
     * report parameter.
     *
     * @param parameters a Collection of ReportParam
     */
    public boolean validateParameters(Collection parameters) {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public Report createReport(ReportDefinition reportDefinition) {
        getAuthzManager().checkPermission(ReportFunctions.REPORT_FUNCTION_CREATE,
                getIdManager().getId(ToolManager.getCurrentPlacement().getId()));

        Report report = new Report(reportDefinition);

        //Create the report parameters
        createReportParameters(report);

        Session s = SessionManager.getCurrentSession();
        report.setUserId(s.getUserId());
        report.setCreationDate(new Date());

        return report;
    }

    /**
     * This function generates the sql connection.
     * If the dataSource connection fails then we want to fail over to
     * the hibernate session connection.  If the usesWarehouse param is null then
     * the connection should default to use the warehouse
     *
     * @return Connection
     * @throws HibernateException
     * @throws SQLException
     */
    public Connection getConnection(Boolean useWarehouse) throws HibernateException, SQLException {
        Connection con = null;

        if (useWarehouse == null) {
            con = getDataSourceUseWarehouse(true).getConnection();
        } else {
            con = getDataSourceUseWarehouse(useWarehouse.booleanValue()).getConnection();
        }

        canCloseConnection = true;

        //fail over to the session connection
        if (con == null) {
            org.hibernate.Session session = getSession();

            con = session.connection();
            //as of hibernate 3.1 you must close your connections
            //http://www.hibernate.org/250.html
            canCloseConnection = true;
        }

        return con;
    }

    /**
     * This closes the database connection if it was pulled from the
     * data warehouse.  (IOW, doesn't close if the connection came from
     * the hibernate session)
     *
     * @param connection
     * @throws SQLException
     * @deprecated ? should this method even be used now that both sources require connections to be closed?
     */
    public void closeConnection(Connection connection) throws SQLException {
        if (canCloseConnection) {
            connection.close();
        }
    }

    protected String escapeCommas(String value) {
        return value.replaceAll(",", "<<comma>>");
    }

    /**
     * gathers the data for dropdown/list box.
     *
     * @return String
     */
    public String generateSQLParameterValue(ReportParam reportParam, List<ReportParam> reportParams) {
        Connection connection = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        String results = "[]";
        StringBuilder strbuffer = new StringBuilder();
        try {
            connection = getConnection(
                    reportParam.getReportDefinitionParam().getReportDefinition().getUsesWarehouse());
            stmt = connection
                    .prepareStatement(replaceSystemValues(reportParam.getReportDefinitionParam().getValue()));

            buildPreparedStatementParameterList(reportParams, stmt,
                    reportParam.getReportDefinitionParam().getReportDefinition());

            rs = stmt.executeQuery();
            strbuffer.append("[");
            int columns = rs.getMetaData().getColumnCount();
            while (rs.next()) {
                if (columns >= 2) {
                    strbuffer.append("(");
                }
                if (columns >= 1) {
                    String rsVal = rs.getString(1);
                    ParameterResultsPostProcessor prpp = reportParam.getReportDefinitionParam()
                            .getResultProcessor();
                    if (prpp != null) {
                        rsVal = prpp.process(rsVal);
                    }
                    strbuffer.append(escapeCommas(rsVal));
                }
                if (columns >= 2) {
                    strbuffer.append(";");
                    strbuffer.append(escapeCommas(rs.getString(2)));
                    strbuffer.append(")");
                }
                strbuffer.append(",");
            }
            strbuffer.append("]");
            results = strbuffer.toString();
        } catch (SQLException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        } catch (HibernateException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        } catch (ParseException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        } finally {
            //ensure that the results set is clsoed
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("loadArtifactTypes(String, Map) caught " + e);
                    }
                }
            }
            //ensure that the stmt is closed
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("loadArtifactTypes(String, Map) caught " + e);
                    }
                }
            }
            if (connection != null) {
                try {
                    closeConnection(connection);
                } catch (Exception e) {
                    if (logger.isDebugEnabled()) {
                        logger.error("", e);
                    }
                }
            }
        }
        return results;
    }

    /**
     * gets the xsl tranform file, does the transform on the Results,
     * then applies the post processor.
     * <p/>
     * The result is a file being placed into the output Stream.
     *
     * @param params
     * @param out
     * @throws IOException
     */
    public String packageForDownload(Map params, OutputStream out) throws IOException {
        ReportResult result = getCurrentResult();

        String exportResultsId = ((String[]) params.get(EXPORT_XSL_ID))[0];
        ReportXsl xslt = result.getReport().getReportDefinition().findReportXslByRuntimeId(exportResultsId);
        xslt.setReportDefinition(result.getReport().getReportDefinition());
        String fileData = transform(result, xslt);

        if (xslt.getResultsPostProcessor() != null) {
            out.write(xslt.getResultsPostProcessor().postProcess(fileData));
        } else {
            out.write(fileData.getBytes());
        }

        //Blank filename for now -- no more dangerous, since the request is in the form of a filename
        return "";

    }

    private List<ReportParam> runProcessedReportParams(List<ReportDefinitionParam> defProcessedParams,
            List<ReportParam> reportParams, Report report, ReportDefinition rd) {
        List<ReportParam> retParams = new ArrayList<ReportParam>();
        retParams.addAll(reportParams);

        if (defProcessedParams != null) {
            Iterator<ReportDefinitionParam> iter = defProcessedParams.iterator();

            while (iter.hasNext()) {
                ReportDefinitionParam rdp = (ReportDefinitionParam) iter.next();
                rdp.setReportDefinition(rd);
                ReportParam rp = new ReportParam();

                rp.setReportDefinitionParam(rdp);
                rp.setReport(report);

                //   if the parameter is static then copy the value, otherwise it is filled by user

                rp.setValue(replaceSystemValues(rdp.getValue()));
                rp.setValue(generateSQLParameterValue(rp, reportParams));

                retParams.add(rp);
                report.getReportParams().add(rp);
            }
        }

        return retParams;
    }

    /**
     * {@inheritDoc}
     */
    public ReportResult generateResults(Report report) throws ReportExecutionException {
        ReportResult rr = new ReportResult();

        ReportDefinition rd = report.getReportDefinition();
        if (rd == null) {
            report.connectToDefinition(getReportDefinitions());
            rd = report.getReportDefinition();
        }
        rr.setCreationDate(new Date());
        Element reportElement = new Element("reportResult");
        reportElement.addNamespaceDeclaration(Namespace.getNamespace("xs", "http://www.w3.org/2001/XMLSchema"));
        Document document = new Document(reportElement);

        //   replace the parameters with the values
        List reportParams = report.getReportParams();

        List<ReportParam> processedParams = runProcessedReportParams(rd.getReportDefinitionProcessedParams(),
                reportParams, report, rd);

        int index = -1;
        for (Iterator i = rd.getQuery().iterator(); i.hasNext();) {
            StringBuilder nextQuery = new StringBuilder(replaceSystemValues((String) i.next()));
            if (index > -1) {
                // <extraReportResult index="0">, <extraReportResult index="1"> etc.
                Element currentReportResult = new Element("extraReportResult");
                currentReportResult.setAttribute("index", String.valueOf(index));
                reportElement.addContent(currentReportResult);
                executeQuery(nextQuery, processedParams, report, currentReportResult, rr, rd, false);
            } else {
                executeQuery(nextQuery, processedParams, report, reportElement, rr, rd, true);
            }
            index++;
        }

        rr.setCreationDate(new Date());
        rr.setReport(report);
        rr.setTitle(report.getTitle());
        rr.setKeywords(report.getKeywords());
        rr.setDescription(report.getDescription());
        rr.setUserId(report.getUserId());
        rr.setXml((new XMLOutputter()).outputString(document));

        rr = postProcessResult(rd, rr);

        return rr;

    }

    private void buildPreparedStatementParameterList(List reportParams, PreparedStatement stmt, ReportDefinition rd)
            throws SQLException, ParseException {
        if (reportParams != null) {
            Iterator iter = reportParams.iterator();
            int paramIndex = 0;

            //   loop through all the parameters and find in query for replacement
            while (iter.hasNext()) {

                //   get the paremeter and associated parameter definition
                ReportParam rp = (ReportParam) iter.next();
                ReportDefinitionParam rdp = rp.getReportDefinitionParam();
                if (ReportDefinitionParam.VALUE_TYPE_MULTI_OF_SET.equals(rdp.getValueType())
                        || ReportDefinitionParam.VALUE_TYPE_MULTI_OF_QUERY.equals(rdp.getValueType())) {

                    for (Iterator i = rp.getListValue().iterator(); i.hasNext();) {
                        stmt.setString(paramIndex + 1, i.next().toString());
                        paramIndex++;
                    }

                } else if (rp.getValue() == null) {
                    throw new RuntimeException(
                            "The Report Parameter Value was blank.  Offending parameter: " + rdp.getParamName());
                } else {
                    String value = rp.getValue();

                    //   Dates need to be formatted from user format to database format
                    if (ReportDefinitionParam.TYPE_DATE.equals(rdp.getType())) {
                        value = dbDateFormat.format(userDateFormat.parse(rp.getValue()));
                        if ("oracle".equals(rd.getVendor())) {
                            SimpleDateFormat oracleFormat = new SimpleDateFormat("dd-MMM-yyyy");
                            value = oracleFormat.format(userDateFormat.parse(rp.getValue()));
                        }
                    }
                    stmt.setString(paramIndex + 1, value);
                    paramIndex++;
                }

            }
        }
    }

    /**
     * executes the query converting the results into xml and updating the report
     *
     * @param query
     * @param reportParams
     * @param report
     * @param reportElement
     * @param rr
     * @param rd
     * @param isFirstResult - true if this is the first sql query in the report, otherwise this should be false
     */
    protected void executeQuery(StringBuilder query, List reportParams, Report report, Element reportElement,
            ReportResult rr, ReportDefinition rd, boolean isFirstResult) {
        //     get the query from the Definition and replace the values
        //     no should be able to put in a system parameter into a report parameter and have it work
        //             so replace the system values before processing the report parameters
        Connection connection = null;
        PreparedStatement stmt = null;

        try {
            connection = getConnection(report.getReportDefinition().getUsesWarehouse());

            query = replaceForMultiSet(query, reportParams);
            stmt = connection.prepareStatement(query.toString());
            //If there are params, place them with values in the query
            buildPreparedStatementParameterList(reportParams, stmt, rd);

            // run the query
            ResultSet rs = null;
            int resultSetIndex = 0;

            rs = stmt.executeQuery();

            boolean makeUppercase = true;
            if (forceColumnLabelUppercase != null) {
                makeUppercase = forceColumnLabelUppercase.booleanValue();
            }

            String forceProperty = ServerConfigurationService.getString("sakai.reports.forceColumnLabelUppercase");
            if (forceProperty != null && forceProperty.length() > 0) {
                makeUppercase = Integer.parseInt(forceProperty) == 1;
            }

            int columns = rs.getMetaData().getColumnCount();

            String[] columnNames = new String[columns];

            for (int i = 0; i < columns; i++) {
                columnNames[i] = rs.getMetaData().getColumnLabel(i + 1);
                if (makeUppercase) {
                    columnNames[i] = columnNames[i].toUpperCase();
                }
            }

            if (isFirstResult) {
                Element docAttrNode = new Element("attributes");
                Element attr = new Element("title");
                attr.setText(report.getTitle());
                reportElement.addContent(attr);

                attr = new Element("description");
                attr.setText(report.getDescription());
                reportElement.addContent(attr);

                attr = new Element("keywords");
                attr.setText(report.getKeywords());
                reportElement.addContent(attr);

                attr = new Element("runDate");
                attr.setText(rr.getCreationDate().toString());
                reportElement.addContent(attr);

                attr = new Element("isWarehouseReport");
                attr.setText(report.getReportDefinition().getUsesWarehouse().toString());
                reportElement.addContent(attr);

                attr = new Element("isLiveReport");
                attr.setText(Boolean.toString(report.getIsLive()));
                reportElement.addContent(attr);

                attr = new Element("isSavedReport");
                attr.setText(Boolean.toString(report.getIsSaved()));
                reportElement.addContent(attr);

                attr = new Element("accessUrl");
                attr.setText(ServerConfigurationService.getAccessUrl());
                reportElement.addContent(attr);
                reportElement.addContent(docAttrNode);
            }

            Element paramsNode = new Element("parameters");

            if (reportParams != null) {
                Iterator iter = report.getReportParams().iterator();

                //   loop through all the parameters
                while (iter.hasNext()) {

                    //   get the paremeter and associated parameter definition
                    ReportParam rp = (ReportParam) iter.next();
                    ReportDefinitionParam rdp = rp.getReportDefinitionParam();

                    Element paramNode = new Element("parameter");

                    paramNode.setAttribute("title", rdp.getTitle());
                    paramNode.setAttribute("name", rdp.getParamName());
                    paramNode.setAttribute("type", rdp.getType());

                    paramNode.setText(rp.getValue());

                    paramsNode.addContent(paramNode);
                }
            }
            reportElement.addContent(paramsNode);

            Element columnsNode = new Element("columns");
            for (int i = 0; i < columnNames.length; i++) {

                Element column = new Element("column");
                column.setAttribute("colIndex", "" + i);
                column.setAttribute("title", columnNames[i]);
                columnsNode.addContent(column);
            }
            reportElement.addContent(columnsNode);

            Element datarowsNode = new Element("data");
            while (rs.next()) {

                Element dataRow = new Element("datarow");

                dataRow.setAttribute("index", "" + resultSetIndex++);
                datarowsNode.addContent(dataRow);

                for (int i = 0; i < columns; i++) {

                    String data = rs.getString(i + 1);

                    Element columnNode = new Element("element");

                    dataRow.addContent(columnNode);

                    columnNode.setAttribute("colIndex", "" + i);
                    columnNode.setAttribute("colName", columnNames[i]);

                    if (data == null) {
                        columnNode.setAttribute("isNull", "true");
                        data = "";
                    }
                    columnNode.addContent(new CDATA(data));
                }
            }
            reportElement.addContent(datarowsNode);

        } catch (SQLException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        } catch (ParseException e) {
            logger.error("", e);
            throw new ReportExecutionException(e);
        } catch (HibernateException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        } finally {
            try {
                stmt.close();
            } catch (Exception e) {
                logger.error("", e);
            }
            try {
                closeConnection(connection);
            } catch (Exception e) {
                logger.error("", e);
            }
        }

    }

    /**
     * applies all the post processing filters and returns the processed results
     *
     * @param rd
     * @param rr
     * @return results
     */
    protected ReportResult postProcessResult(ReportDefinition rd, ReportResult rr) {
        List resultProcessors = rd.getResultProcessors();
        if (resultProcessors != null) {
            for (Iterator i = resultProcessors.iterator(); i.hasNext();) {
                ResultProcessor processor = (ResultProcessor) i.next();
                rr = processor.process(rr);
            }
        }
        return rr;
    }

    public StringBuilder replaceForMultiSet(StringBuilder inQuery, List reportParams) {
        if (reportParams == null) {
            return inQuery;
        }
        Iterator iter = reportParams.iterator();
        //   loop through all the parameters and find in query for replacement
        while (iter.hasNext()) {

            //   get the paremeter and associated parameter definition
            ReportParam rp = (ReportParam) iter.next();
            ReportDefinitionParam rdp = rp.getReportDefinitionParam();
            if (ReportDefinitionParam.VALUE_TYPE_MULTI_OF_SET.equals(rdp.getValueType())
                    || ReportDefinitionParam.VALUE_TYPE_MULTI_OF_QUERY.equals(rdp.getValueType())) {

                if (rp.getListValue().size() > 1) {
                    int index = inQuery.indexOf("(?)");
                    inQuery.delete(index, index + 3);
                    StringBuilder tempString = new StringBuilder("(");
                    for (int i = 0; i < rp.getListValue().size(); i++) {
                        tempString.append("?,");
                    }
                    tempString.delete(tempString.length() - 1, tempString.length());
                    tempString.append(") ");
                    inQuery.insert(index, tempString);
                }
            }
        }
        return inQuery;
    }

    /**
     * {@inheritDoc}
     */
    public String replaceSystemValues(String inString) {
        UserDirectoryService userDirectoryService = org.sakaiproject.user.cover.UserDirectoryService.getInstance();

        Session s = SessionManager.getCurrentSession();
        User user = userDirectoryService.getCurrentUser();
        String worksiteId = ToolManager.getCurrentPlacement().getContext(); // current site id
        Site site = getCurrentWorksite(worksiteId);

        Map map = new HashMap();
        map.put("{userid}", Validator.escapeSql(s.getUserId()));
        //system values are stored in session if report is scheduled through quartz
        if (s.getAttribute("toolid") == null) {
            UserDirectoryService dirServ = org.sakaiproject.user.cover.UserDirectoryService.getInstance();
            User u = dirServ.getCurrentUser();
            map.put("{userdisplayname}", Validator.escapeSql(u.getDisplayName()));
            map.put("{useremail}", Validator.escapeSql(u.getEmail()));
            map.put("{userfirstname}", Validator.escapeSql(u.getFirstName()));
            map.put("{userlastname}", Validator.escapeSql(u.getLastName()));
            map.put("{worksiteid}", Validator.escapeSql(ToolManager.getCurrentPlacement().getContext()));
            map.put("{toolid}", Validator.escapeSql(ToolManager.getCurrentPlacement().getId()));
        } else {
            map.put("{userdisplayname}", Validator.escapeSql((String) s.getAttribute("userdisplayname")));
            map.put("{useremail}", Validator.escapeSql((String) s.getAttribute("useremail")));
            map.put("{userfirstname}", Validator.escapeSql((String) s.getAttribute("userfirstname")));
            map.put("{userlastname}", Validator.escapeSql((String) s.getAttribute("userlastname")));
            map.put("{worksiteid}", Validator.escapeSql((String) s.getAttribute("worksiteid")));
            map.put("{toolid}", Validator.escapeSql((String) s.getAttribute("toolid")));
        }

        Iterator iter = map.keySet().iterator();
        StringBuilder str = new StringBuilder(inString);

        //   loop through all the parameters and find in query for replacement
        while (iter.hasNext()) {

            //   get the parameter and associated parameter definition
            String key = (String) iter.next();

            int i = str.indexOf(key);

            //   Loop until no instances exist
            while (i != -1) {

                //   replace the parameter with the value
                str.delete(i, i + key.length());
                str.insert(i, (String) map.get(key));

                //   look for a second instance
                i = str.indexOf(key);
            }
        }

        String string = str.toString();

        // create a list of the supported bean objects whose values can be replaced
        Map beans = new HashMap();
        beans.put("{session.attribute.", s);
        beans.put("{site.property.", site);
        beans.put("{user.property.", user);
        string = replaceSystemValues(string, beans);

        beans = new HashMap();
        beans.put("{session.", s);
        beans.put("{site.", site);
        beans.put("{user.", user);
        string = replaceSystemValues(string, beans);

        return string;
    }

    private Site getCurrentWorksite(String worksiteId) {
        Site site = null;
        try {
            if (worksiteId != null && worksiteId.trim().length() != 0)
                site = SiteService.getSite(worksiteId);
        } catch (IdUnusedException ex) {
            // do nothing.  just return null.
        }
        return site;
    }

    public String replaceSystemValues(String string, Map beans) {

        StringBuffer buffer = new StringBuffer(string);
        Set beanNames = beans.keySet();
        for (Iterator it = beanNames.iterator(); it.hasNext();) {
            String beanName = (String) it.next();
            // see if the string contains reference(s) to the supported beans
            for (int i = buffer.indexOf(beanName), j = beanName.length(); i != -1; i = buffer.indexOf(beanName)) {
                // if it does, parse the property of the bean the report query references
                int k = buffer.indexOf("}", i + j);
                if (k == -1)
                    throw new RuntimeException("Missing closing brace \"}\" in report query: " + string);
                String property = buffer.substring(i + j, k);

                // construct the bean property's corresponding "getter" method
                String getter = null;
                String param = null;
                if (beanName.indexOf(".attribute.") != -1) {
                    getter = "getAttribute";
                    param = property;
                } else if (beanName.indexOf(".property.") != -1) {
                    getter = "getProperties";
                    param = null;
                } else {
                    getter = "get" + Character.toUpperCase(property.charAt(0)) + property.substring(1);
                    param = null;
                }

                try {
                    // use reflection to invoke the method on the bean
                    Object bean = beans.get(beanName);
                    Class clasz = bean.getClass();
                    Class[] args = param == null ? (Class[]) null : new Class[] { String.class };
                    Method method = clasz.getMethod(getter, args);
                    Object result = method.invoke(bean, (param == null ? (Object[]) null : new Object[] { param }));

                    if (beanName.indexOf(".property.") != -1) {
                        clasz = org.sakaiproject.entity.api.ResourceProperties.class;
                        getter = "getProperty";
                        args = new Class[] { String.class };
                        param = property;
                        method = clasz.getMethod(getter, args);
                        result = method.invoke(result, new Object[] { param });
                    }

                    // replace the bean expression in the report query with the actual value of calling the bean's corresponding getter method
                    buffer.delete(i, k + 1);
                    buffer.insert(i,
                            (result == null ? "null"
                                    : result instanceof Time ? ((Time) result).toStringSql()
                                            : result.toString().replaceAll("'", "''")));
                } catch (Exception ex) {
                    throw new RuntimeException(ex.getMessage(), ex);
                }
            }
        }
        return buffer.toString();
    }

    /**
     * {@inheritDoc}
     */
    public String transform(ReportResult reportResult, ReportXsl reportXsl) {
        try {

            JDOMResult result = new JDOMResult();
            SAXBuilder builder = new SAXBuilder();
            StreamSource xsltSource;
            if (reportXsl.getResource() == null) {
                xsltSource = new StreamSource(loadXslFromDB(reportXsl));
            } else {
                xsltSource = new StreamSource(reportXsl.getResource().getInputStream());
            }
            Transformer transformer = TransformerFactory.newInstance().newTransformer(xsltSource);
            Document rootElement = builder.build(new StringReader(reportResult.getXml()));

            ByteArrayOutputStream sourceOut = new ByteArrayOutputStream();
            StreamResult resultstream = new StreamResult(sourceOut);

            transformer.transform(new JDOMSource(rootElement), resultstream);

            return sourceOut.toString();

        } catch (Exception e) {
            logger.error("", e);
            throw new RuntimeException(e);
        }
    }

    public InputStream loadXslFromDB(ReportXsl reportXsl) {
        ReportDefinitionXmlFile reportDef = getReportDefinition(reportXsl.getReportDefinition().getIdString());

        ByteArrayInputStream inputStream = null;
        for (Iterator i = reportDef.getReportXslFiles().iterator(); i.hasNext();) {
            ReportXslFile xslFile = (ReportXslFile) i.next();
            if (xslFile.getReportXslFileRef().equals(reportXsl.getXslLink())) {
                inputStream = new ByteArrayInputStream(xslFile.getXslFile());
                return inputStream;
            }
        }
        throw new RuntimeException("can't find report xslfile: reportDef=["
                + reportXsl.getReportDefinition().getReportDefId().toString() + "], xslLink=["
                + reportXsl.getXslLink() + "]");
    }

    private void writeFile(String fileString, String fileName, String contentType) {
        FacesContext faces = FacesContext.getCurrentInstance();
        HttpServletResponse response = (HttpServletResponse) faces.getExternalContext().getResponse();
        protectAgainstInstantDeletion(response);
        response.setContentType(contentType);
        response.setHeader("Content-disposition", "attachment; filename=" + fileName + ".csv");
        response.setContentLength(fileString.length());
        OutputStream out = null;
        try {
            out = response.getOutputStream();
            out.write(fileString.getBytes());
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        faces.responseComplete();
    }

    /**
     * THIS IS TAKEN FROM GRADEBOOK: org.sakai.tool.gradebook.ui.ExportBean
     * <p/>
     * Try to head off a problem with downloading files from a secure HTTPS
     * connection to Internet Explorer.
     * <p/>
     * When IE sees it's talking to a secure server, it decides to treat all hints
     * or instructions about caching as strictly as possible. Immediately upon
     * finishing the download, it throws the data away.
     * <p/>
     * Unfortunately, the way IE sends a downloaded file on to a helper
     * application is to use the cached copy. Having just deleted the file,
     * it naturally isn't able to find it in the cache. Whereupon it delivers
     * a very misleading error message like:
     * "Internet Explorer cannot download roster from sakai.yoursite.edu.
     * Internet Explorer was not able to open this Internet site. The requested
     * site is either unavailable or cannot be found. Please try again later."
     * <p/>
     * There are several ways to turn caching off, and so to be safe we use
     * several ways to turn it back on again.
     * <p/>
     * This current workaround should let IE users save the files to disk.
     * Unfortunately, errors may still occur if a user attempts to open the
     * file directly in a helper application from a secure web server.
     * <p/>
     * TODO Keep checking on the status of this.
     */
    private static void protectAgainstInstantDeletion(HttpServletResponse response) {
        response.reset(); // Eliminate the added-on stuff
        response.setHeader("Pragma", "public"); // Override old-style cache control
        response.setHeader("Cache-Control", "public, must-revalidate, post-check=0, pre-check=0, max-age=0"); // New-style
    }

    /**
     * {@inheritDoc}
     */
    public void saveReportResult(ReportResult result) {
        getHibernateTemplate().saveOrUpdate(result.getReport());
        getHibernateTemplate().saveOrUpdate(result);

        //   the user can't save results that have already been saved
        result.getReport().setIsSaved(true);
        result.setIsSaved(true);
    }

    /**
     * {@inheritDoc}
     */
    public void saveReport(Report report) {
        getHibernateTemplate().saveOrUpdate(report);

        //   the user can't save reports that have already been saved
        report.setIsSaved(true);
    }

    /**
     * {@inheritDoc}
     */
    public void deleteReportResult(ReportResult result) {

        checkPermission(ReportFunctions.REPORT_FUNCTION_DELETE);

        getHibernateTemplate().delete(result);

        // if we are deleting the result, then if the report it came from is not on display then delete the report too
        if (!result.getReport().getDisplay() || !result.getReport().getIsLive()) {
            deleteReport(result.getReport(), false);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void deleteReport(Report report, boolean deactivate) {
        boolean deleteAction = false, deactivateAction = false;

        checkPermission(ReportFunctions.REPORT_FUNCTION_DELETE);

        report = (Report) getHibernateTemplate().get(Report.class, report.getReportId());

        List results = getHibernateTemplate().findByNamedQuery("findResultsByReport", report);

        if (report.getIsLive()) {
            if (results.size() == 0) {
                deleteAction = true;
            } else if (deactivate) {
                deactivateAction = true;
            }
        } else { //the report is not live so delete any report results
            for (Iterator i = results.iterator(); i.hasNext();) {
                getHibernateTemplate().delete(i.next());
            }
            deleteAction = true;
        }

        if (deleteAction) {
            getHibernateTemplate().delete(report);
        } else if (deactivateAction) {
            report.setDisplay(false);
            getHibernateTemplate().saveOrUpdate(report);
        }
    }

    public String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public SecurityService getSecurityService() {
        return securityService;
    }

    public void setSecurityService(SecurityService securityService) {
        this.securityService = securityService;
    }

    private Site getCurrentSite() {
        try {
            if (ToolManager.getCurrentPlacement() != null) {
                return SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
            }
            //if this job is running because of scheduler
            org.sakaiproject.tool.api.Session sakaiSession = SessionManager.getCurrentSession();
            return SiteService.getSite((String) sakaiSession.getAttribute("worksiteid"));

        } catch (IdUnusedException iue) {
            return null;
        }
    }

    /**
     * Returns the type of current worksite
     *
     * @return String
     */
    private String getCurrentSiteType() {
        return getCurrentSite() != null ? getCurrentSite().getType() : "";
    }

    public AuthorizationFacade getAuthzManager() {
        return authzManager;
    }

    public void setAuthzManager(AuthorizationFacade authzManager) {
        this.authzManager = authzManager;
    }

    protected void checkPermission(String function) {
        getAuthzManager().checkPermission(function,
                getIdManager().getId(ToolManager.getCurrentPlacement().getId()));
    }

    /**
     * {@inheritDoc}
     */
    public Map getAuthorizationsMap() {
        return new AuthZMap(getAuthzManager(), ReportFunctions.REPORT_FUNCTION_PREFIX,
                getIdManager().getId(ToolManager.getCurrentPlacement().getId()));
    }

    protected boolean can(String function) {
        return new Boolean(getAuthzManager().isAuthorized(function,
                getIdManager().getId(ToolManager.getCurrentPlacement().getId()))).booleanValue();
    }

    /**
     * {@inheritDoc}
     */
    public boolean isMaintaner() {
        return new Boolean(getAuthzManager().isAuthorized(WorksiteManager.WORKSITE_MAINTAIN,
                getIdManager().getId(ToolManager.getCurrentPlacement().getContext()))).booleanValue();
    }

    /**
     * {@inheritDoc}
     */
    public void checkEditAccess() {
        checkPermission(ReportFunctions.REPORT_FUNCTION_EDIT);
    }

    public DataSource getSakaiDataSource() {
        return sakaiDataSource;
    }

    public void setSakaiDataSource(DataSource sakaiDataSource) {
        this.sakaiDataSource = sakaiDataSource;
    }

    public Boolean getForceColumnLabelUppercase() {
        return forceColumnLabelUppercase;
    }

    public void setForceColumnLabelUppercase(Boolean forceColumnLabelUppercase) {
        this.forceColumnLabelUppercase = forceColumnLabelUppercase;
    }

    private ReportDefinitionXmlFile importReport(ContentResource resource) {
        ReportDefinitionXmlFile bean = null;

        try {

            bean = new ReportDefinitionXmlFile(resource);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

    public ContentHostingService getContentHosting() {
        return contentHosting;
    }

    public void setContentHosting(ContentHostingService contentHosting) {
        this.contentHosting = contentHosting;
    }

    public List getDefinedDefintions() {
        return definedDefintions;
    }

    public void setDefinedDefintions(List definedDefintions) {
        this.definedDefintions = definedDefintions;
    }

    public boolean importResource(Id worksiteId, String nodeId)
            throws UnsupportedFileTypeException, ImportException, RuntimeException {

        String id = getContentHosting().resolveUuid(nodeId);
        try {
            ContentResource resource = getContentHosting().getResource(id);
            MimeType mimeType = new MimeType(resource.getContentType());

            if (mimeType.equals(new MimeType("application/xml")) || mimeType.equals(new MimeType("text/xml"))) {
                ListableBeanFactory beanFactory = new XmlBeanFactory(new ByteArrayResource(resource.getContent()),
                        getBeanFactory());
                ReportDefinitionXmlFile bean = importReport(resource);
                if (bean != null) {
                    saveReportDef(bean, beanFactory);
                }
                return bean != null;
            } else {
                throw new UnsupportedFileTypeException("Unsupported file type");
            }

        } catch (ServerOverloadException soe) {
            logger.warn(soe);

        } catch (PermissionException pe) {
            logger.warn("Failed loading content: no permission to view file", pe);
        } catch (TypeException te) {
            logger.warn("Wrong type", te);
        } catch (IdUnusedException iue) {
            logger.warn("UnusedId: ", iue);
        }
        return false;
    }

    public void saveReportDef(ReportDefinitionXmlFile xmlFile, ListableBeanFactory beanFactory)
            throws RuntimeException {

        ReportDefinition reportDef = getReportDefBean(beanFactory);
        List reportDefList = new ArrayList();
        reportDefList.add(reportDef);
        xmlFile.setReportDefId(reportDef.getIdString());
        xmlFile.setReportXslFiles(processXSLFiles(reportDef, xmlFile));
        getHibernateTemplate().saveOrUpdate(xmlFile);
    }

    public Set processXSLFiles(ReportDefinition reportDef, ReportDefinitionXmlFile xmlFile)
            throws RuntimeException {
        Set xslsList = new HashSet();

        ReportXsl defaultXsl = reportDef.getDefaultXsl();

        if (defaultXsl == null) {
            return xslsList;
        } else {
            List xsls = reportDef.getXsls();
            for (Iterator i = xsls.iterator(); i.hasNext();) {
                ReportXsl xsl = (ReportXsl) i.next();
                ReportXslFile xslFile = new ReportXslFile(xsl, getContentHosting());
                xslFile.setReportXslFileRef(xsl.getXslLink());
                xslFile.setXslFileHash(DigestUtils.md5Hex(xslFile.getXslFile()));

                xslsList.add(xslFile);
            }

        }

        return xslsList;

    }

    public void deleteReportDefXmlFile(ReportDefinition reportDef) {

        checkPermission(ReportFunctions.REPORT_FUNCTION_DELETE);
        List<ReportDefinitionXmlFile> defs = getHibernateTemplate()
                .find("from ReportDefinitionXmlFile r where reportDefId = ?", reportDef.getIdString());
        for (ReportDefinitionXmlFile def : defs) {
            getHibernateTemplate().delete(def);
        }

        List<Report> reports = getHibernateTemplate().find("from Report r where reportDefIdMark = ?",
                reportDef.getIdString());
        for (Report report : reports) {
            List<ReportResult> results = getHibernateTemplate()
                    .find("from ReportResult r where r.report.reportId = ?", report.getReportId());
            for (ReportResult rr : results) {
                getHibernateTemplate().delete(rr);
            }
            getHibernateTemplate().delete(report);
        }
    }

    public ReportDefinition getReportDefBean(ListableBeanFactory beanFactory) {
        Map beanMap = beanFactory.getBeansOfType(ReportDefinition.class);
        for (Iterator i = beanMap.values().iterator(); i.hasNext();) {
            return (ReportDefinition) i.next();
        }
        return null;
    }

    public void saveXslFile(ReportXslFile reportXslFile) {
        getHibernateTemplate().saveOrUpdate(reportXslFile);
    }

    protected void initDefinedReportDefinitions() {
        getSecurityService().pushAdvisor(new AllowAllSecurityAdvisor());

        org.sakaiproject.tool.api.Session sakaiSession = SessionManager.getCurrentSession();
        String userId = sakaiSession.getUserId();
        sakaiSession.setUserId("admin");
        sakaiSession.setUserEid("admin");
        List definitions = new ArrayList();

        try {
            for (Iterator i = getDefinedDefintions().iterator(); i.hasNext();) {
                ReportsDefinitionWrapper wrapper = (ReportsDefinitionWrapper) i.next();
                wrapper.setParentClass(getClass());
                definitions.add(processDefinedDefinition(wrapper));
            }

        } finally {
            getSecurityService().popAdvisor();
            sakaiSession.setUserEid(userId);
            sakaiSession.setUserId(userId);
        }

    }

    protected void convert24to25Reports() {
        getSecurityService().pushAdvisor(new AllowAllSecurityAdvisor());

        org.sakaiproject.tool.api.Session sakaiSession = SessionManager.getCurrentSession();
        String userId = sakaiSession.getUserId();
        sakaiSession.setUserId("admin");
        sakaiSession.setUserEid("admin");

        Transaction tx = getSession().beginTransaction();
        try {
            Transformer trans = createTemplate("/org/sakaiproject/reports/conversion/reports24to25.xsl")
                    .newTransformer();
            for (Iterator<ReportDefinitionXmlFile> i = get24Defintions().iterator(); i.hasNext();) {
                process24Definition(i.next(), trans);
            }

        } catch (MalformedURLException e) {
            tx.rollback();
            logger.error("error migrating 2.4 report definitions", e);
        } catch (TransformerConfigurationException e) {
            tx.rollback();
            logger.error("error migrating 2.4 report definitions", e);
        } catch (TransformerException e) {
            tx.rollback();
            logger.error("error migrating 2.4 report definitions", e);
        } finally {
            if (!tx.wasRolledBack()) {
                tx.commit();
            }
            getSecurityService().popAdvisor();
            sakaiSession.setUserEid(userId);
            sakaiSession.setUserId(userId);
        }

    }

    protected void process24Definition(ReportDefinitionXmlFile reportDefinitionXmlFile, Transformer trans)
            throws TransformerException {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Document doc = reportDefinitionXmlFile.getXml();
        trans.transform(new JDOMSource(doc), new StreamResult(bos));
        reportDefinitionXmlFile.setXmlFile(bos.toByteArray());
        getHibernateTemplate().saveOrUpdate(reportDefinitionXmlFile);
    }

    protected List<ReportDefinitionXmlFile> get24Defintions() {
        return getHibernateTemplate().findByNamedQuery("find24ReportDefinitions");
    }

    protected Templates createTemplate(String transformPath)
            throws MalformedURLException, TransformerConfigurationException {

        InputStream stream = getClass().getResourceAsStream(transformPath);
        URL url = getClass().getResource(transformPath);
        String urlPath = url.toString();
        String systemId = urlPath.substring(0, urlPath.lastIndexOf('/') + 1);
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Templates templates = transformerFactory.newTemplates(new StreamSource(stream, systemId));
        return templates;
    }

    public void addReportDefinition(ReportsDefinitionWrapper reportDef) {
        processDefinedDefinition(reportDef);
    }

    protected ReportDefinitionXmlFile processDefinedDefinition(ReportsDefinitionWrapper wrapper) {
        ReportDefinitionXmlFile definition = getReportDefinition(wrapper.getIdValue());

        if (definition == null) {
            definition = new ReportDefinitionXmlFile();
            definition.setReportDefId(wrapper.getIdValue());
        }

        updateDefinition(wrapper, definition);
        return definition;
    }

    protected void updateDefinition(ReportsDefinitionWrapper wrapper, ReportDefinitionXmlFile def) {
        try {

            InputStream stream = wrapper.getParentClass().getResourceAsStream(wrapper.getDefinitionFileLocation());
            if (stream == null) {
                throw new RuntimeException(
                        "Loaded Report Definition failed: " + wrapper.getDefinitionFileLocation());
            }
            Set xslFilesToBeRemoved = new HashSet();

            if (def.getReportXslFiles() != null) {
                xslFilesToBeRemoved.addAll(def.getReportXslFiles());
            } else {
                def.setReportXslFiles(new HashSet());
            }
            def.setXmlFile(readStreamToBytes(
                    wrapper.getParentClass().getResourceAsStream(wrapper.getDefinitionFileLocation())));

            ListableBeanFactory beanFactory = new XmlBeanFactory(
                    new ByteArrayResource(readStreamToBytes(
                            wrapper.getParentClass().getResourceAsStream(wrapper.getDefinitionFileLocation()))),
                    getBeanFactory());
            ReportDefinition repDef = getReportDefBean(beanFactory);
            List xsls = repDef.getXsls();
            for (Iterator i = xsls.iterator(); i.hasNext();) {
                ReportXsl xsl = (ReportXsl) i.next();
                ReportXslFile xslFile = new ReportXslFile();
                if (wrapper.getParentClass().getResourceAsStream(xsl.getXslLink()) != null) {
                    xslFile.setXslFile(
                            readStreamToBytes(wrapper.getParentClass().getResourceAsStream(xsl.getXslLink())));
                    xslFile.setXslFileHash(DigestUtils.md5Hex(xslFile.getXslFile()));
                }
                //xslFile.setReportDefId(repDef.getIdString());
                xslFile.setReportXslFileRef(xsl.getXslLink());
                if (def.getReportXslFiles().contains(xslFile)) {
                    xslFilesToBeRemoved.remove(xslFile);
                } else {
                    def.getReportXslFiles().add(xslFile);
                }
            }

            for (Iterator<ReportXslFile> i = xslFilesToBeRemoved.iterator(); i.hasNext();) {
                ReportXslFile removedFile = i.next();
                def.getReportXslFiles().remove(removedFile);
            }
            getHibernateTemplate().saveOrUpdate(def);
        } catch (Exception e) {
            throw new RuntimeException("Loaded report def failed", e);
        }
    }

    public ReportDefinitionXmlFile getReportDefinition(String id) {
        return (ReportDefinitionXmlFile) getHibernateTemplate().get(ReportDefinitionXmlFile.class, id);
    }

    public Report getReportById(String id) {
        Report report = (Report) getHibernateTemplate().get(Report.class, new IdImpl(id, null));
        report.connectToDefinition(getReportDefinitions());
        return report;
    }

    private byte[] readStreamToBytes(InputStream inStream) throws IOException {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        byte data[] = new byte[10 * 1024];

        int count;
        while ((count = inStream.read(data, 0, 10 * 1024)) != -1) {
            bytes.write(data, 0, count);
        }
        byte[] tmp = bytes.toByteArray();
        bytes.close();
        return tmp;
    }

    // todo figure out how to impl this -- JDE
    public List getReportsByViewer() {
        Agent viewer = getAuthnManager().getAgent();
        Collection reportAuthzs = getAuthzManager().getAuthorizations(viewer, ReportFunctions.REPORT_FUNCTION_VIEW,
                null);

        List results = new ArrayList();
        for (Iterator i = reportAuthzs.iterator(); i.hasNext();) {
            Id resultId = ((Authorization) i.next()).getQualifier();
            List result = getReportResults(resultId);
            for (Iterator iter = result.iterator(); iter.hasNext();) {
                results.add(iter.next());
            }
        }
        return results;
    }

    List getReportResults(Id resultId) {

        boolean viewReports = can(ReportFunctions.REPORT_FUNCTION_VIEW);

        List returned = new ArrayList();

        if (viewReports) {
            List results = getHibernateTemplate().findByNamedQuery("findResultsById", resultId);

            Iterator iter = results.iterator();
            while (iter.hasNext()) {
                ReportResult r = (ReportResult) iter.next();

                r.setIsSaved(true);
            }
            returned.addAll(results);
        }
        return returned;
    }

    protected String createResource(ByteArrayOutputStream bos, String name, String description, String type) {

        ContentResource resource = null;
        ResourcePropertiesEdit resourceProperties = getContentHosting().newResourceProperties();
        resourceProperties.addProperty(ResourceProperties.PROP_DISPLAY_NAME, name);
        resourceProperties.addProperty(ResourceProperties.PROP_DESCRIPTION, description);
        resourceProperties.addProperty(ResourceProperties.PROP_CONTENT_ENCODING, "UTF-8");

        getSecurityService().pushAdvisor(new AllowAllSecurityAdvisor());

        org.sakaiproject.tool.api.Session sakaiSession = SessionManager.getCurrentSession();
        String userId = sakaiSession.getUserId();
        sakaiSession.setUserId(userId);
        sakaiSession.setUserEid(userId);

        try {

            ContentCollectionEdit groupCollection = getContentHosting()
                    .addCollection(getUserCollection().getId() + "savedReports/");
            groupCollection.getPropertiesEdit().addProperty(ResourceProperties.PROP_DISPLAY_NAME, "Saved Reports");
            groupCollection.getPropertiesEdit().addProperty(ResourceProperties.PROP_DESCRIPTION,
                    "Folder for Saved Report Results");
            getContentHosting().commitCollection(groupCollection);
        } catch (IdUsedException e) {
            // ignore... it is already there.
            if (logger.isDebugEnabled()) {
                logger.debug(e);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        try {
            String id = getUserCollection().getId() + "Saved Reports/" + name;
            getContentHosting().removeResource(id);
        } catch (TypeException e) {
            // ignore, must be new
            if (logger.isDebugEnabled()) {
                logger.debug(e);
            }
        } catch (IdUnusedException e) {
            // ignore, must be new
            if (logger.isDebugEnabled()) {
                logger.debug(e);
            }
        } catch (PermissionException e) {
            // ignore, must be new
            if (logger.isDebugEnabled()) {
                logger.debug(e);
            }
        } catch (InUseException e) {
            // ignore, must be new
            if (logger.isDebugEnabled()) {
                logger.debug(e);
            }
        }

        try {
            resource = getContentHosting().addResource(name, getUserCollection().getId() + "savedReports/", 100,
                    type, bos.toByteArray(), resourceProperties, NotificationService.NOTI_NONE);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return "savedReports/" + name;
    }

    public String processSaveResultsToResources(ReportResult reportResult) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Report report = reportResult.getReport();
        report.connectToDefinition(getReportDefinitions());
        ReportXsl xslt = report.getReportDefinition().getDefaultXsl();
        xslt.setReportDefinition(report.getReportDefinition());
        String fileData = transform(reportResult, xslt);

        if (xslt.getResultsPostProcessor() != null) {
            bos.write(xslt.getResultsPostProcessor().postProcess(fileData));
        } else {
            bos.write(fileData.getBytes());
        }
        return createResource(bos, reportResult.getTitle() + ".html", reportResult.getTitle(), "text/html");

    }

    protected ContentCollection getUserCollection() throws TypeException, IdUnusedException, PermissionException {
        User user = org.sakaiproject.user.cover.UserDirectoryService.getCurrentUser();
        String userId = user.getId();
        String wsId = SiteService.getUserSiteId(userId);
        String wsCollectionId = getContentHosting().getSiteCollection(wsId);
        ContentCollection collection = getContentHosting().getCollection(wsCollectionId);
        return collection;
    }

    public SchedulerManager getSchedulerManager() {
        return schedulerManager;
    }

    public void setSchedulerManager(SchedulerManager schedulerManager) {
        this.schedulerManager = schedulerManager;
    }

    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        getSecurityService().pushAdvisor(new AllowAllSecurityAdvisor());
        org.sakaiproject.tool.api.Session sakaiSession = SessionManager.getCurrentSession();
        String sessionUserId = sakaiSession.getUserId();
        try {
            Map details = jobExecutionContext.getJobDetail().getJobDataMap();
            String reportId = (String) details.get("reportId");
            String userId = (String) details.get("userId");
            sakaiSession.setAttribute("userdisplayname", (String) details.get("userdisplayname"));
            sakaiSession.setAttribute("useremail", (String) details.get("useremail"));
            sakaiSession.setAttribute("userfirstname", (String) details.get("userfirstname"));
            sakaiSession.setAttribute("userlastname", (String) details.get("userlastname"));
            sakaiSession.setAttribute("worksiteid", (String) details.get("worksiteid"));
            sakaiSession.setAttribute("toolid", (String) details.get("toolid"));
            sakaiSession.setUserId(userId);
            sakaiSession.setUserEid(userId);
            Report report = getReportById(reportId);
            if (report != null) {
                ReportResult result = generateResults(report);
                result.setIsSaved(true);
                result.setTitle(result.getTitle() + " - " + result.getCreationDate());
                processSaveResultsToResources(result);
                saveReportResult(result);
            } else {
                schedulerManager.getScheduler().deleteJob(jobExecutionContext.getJobDetail().getName(),
                        reportGroup);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            getSecurityService().popAdvisor();
            sakaiSession.setUserEid(sessionUserId);
            sakaiSession.setUserId(sessionUserId);
        }

    }

    public void processDeleteJobs(JobDetail jobDetail) {
        try {
            schedulerManager.getScheduler().deleteJob(jobDetail.getName(), reportGroup);
        } catch (SchedulerException e) {
            logger.error("Scheduler Down");
        }
    }

    public JobDetail processCreateJob(Report report) {
        Scheduler scheduler = getSchedulerManager().getScheduler();
        UserDirectoryService dirServ = org.sakaiproject.user.cover.UserDirectoryService.getInstance();
        User u = dirServ.getCurrentUser();
        if (scheduler == null) {
            logger.error("Scheduler is down!");
        }
        JobDetail jd = null;
        try {

            JobBeanWrapper job = getJobBeanWrapper();
            if (job != null) {

                jd = scheduler.getJobDetail(report.getReportId().toString(), reportGroup);
                if (jd == null) {
                    jd = new JobDetail(report.getReportId().toString(), reportGroup, job.getJobClass(), false, true,
                            true);
                    jd.getJobDataMap().put(JobBeanWrapper.SPRING_BEAN_NAME, job.getBeanId());
                    jd.getJobDataMap().put(JobBeanWrapper.JOB_TYPE, job.getJobType());
                    jd.getJobDataMap().put("reportId", report.getReportId().getValue());
                    jd.getJobDataMap().put("userId", SessionManager.getCurrentSessionUserId());
                    jd.getJobDataMap().put("userdisplayname", u.getDisplayName());
                    jd.getJobDataMap().put("useremail", u.getEmail());
                    jd.getJobDataMap().put("userfirstname", u.getFirstName());
                    jd.getJobDataMap().put("userlastname", u.getLastName());
                    jd.getJobDataMap().put("worksiteid", ToolManager.getCurrentPlacement().getContext());
                    jd.getJobDataMap().put("toolid", ToolManager.getCurrentPlacement().getId());

                    scheduler.addJob(jd, false);
                }
            } else {
                jd = new JobDetail(report.getReportId().toString(), reportGroup, SpringJobBeanWrapper.class, false,
                        true, true);
                scheduler.addJob(jd, false);
            }
        } catch (Exception e) {
            logger.error("Failed to create job");
        }
        return jd;
    }

    public JobBeanWrapper getJobBeanWrapper() {
        return jobBeanWrapper;
    }

    public void setJobBeanWrapper(JobBeanWrapper jobBeanWrapper) {
        this.jobBeanWrapper = jobBeanWrapper;
    }

    public boolean isAutoDdl() {
        return autoDdl;
    }

    public void setAutoDdl(boolean autoDdl) {
        this.autoDdl = autoDdl;
    }

    public boolean isUpgrade24() {
        return upgrade24;
    }

    public void setUpgrade24(boolean upgrade24) {
        this.upgrade24 = upgrade24;
    }

}