de.bps.webservices.clients.onyxreporter.OnyxReporterConnector.java Source code

Java tutorial

Introduction

Here is the source code for de.bps.webservices.clients.onyxreporter.OnyxReporterConnector.java

Source

/**
 * <a href="http://www.openolat.org">
 * OpenOLAT - Online Learning and Training</a><br>
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); <br>
 * you may not use this file except in compliance with the License.<br>
 * You may obtain a copy of the License at the
 * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
 * <p>
 * Unless required by applicable law or agreed to in writing,<br>
 * software distributed under the License is distributed on an "AS IS" BASIS, <br>
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
 * See the License for the specific language governing permissions and <br>
 * limitations under the License.
 * <p>
 * Initial code contributed and copyrighted by<br>
 * BPS Bildungsportal Sachsen GmbH, http://www.bps-system.de
 * <p>
 */
package de.bps.webservices.clients.onyxreporter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;

import org.apache.commons.io.IOUtils;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.UserConstants;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.WebappHelper;
import org.olat.course.nodes.AssessableCourseNode;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.iq.IQEditController;
import org.olat.fileresource.FileResourceManager;
import org.olat.ims.qti.QTIResultSet;
import org.olat.repository.RepositoryEntry;

import de.bps.onyx.plugin.OnyxModule;
import de.bps.onyx.plugin.OnyxResultManager;
import de.bps.security.SSLConfigurationModule;

//<ONYX-705>
public class OnyxReporterConnector {
    //<OLATCE-1124>
    private static final String FOUR = "4";
    private static final String ONE = "1";
    private static final String FIVE = "5";
    //</OLATCE-1124>
    //<OLATCE-1089>
    private static final String NONAME = "NONAME";
    //</OLATCE-1089>
    private String surveyFolderPath;

    private final static OLog log = Tracing.createLoggerFor(OnyxReporterConnector.class);
    private final OnyxReporterClient connector;

    private final Pattern pattern = Pattern.compile("(.*v)(.*)\\.(xml|zip)");

    public OnyxReporterConnector() throws OnyxReporterException {

        //TODO check if available
        if (isServiceAvailable(OnyxReporterTarget.getTarget())) {
            connector = new OnyxReporterClient();
        } else {
            //<OLATCE-1124>
            log.error("OnyxReporterService is unavailable! Tried to use: " + OnyxReporterTarget.getTarget());
            //</OLATCE-1124>
            throw new OnyxReporterException("Unable to connect to OnyxReporter");
        }
    }

    /**
     * Delivers a map with all possible outcome-variables of this onyx test.
     * @param node The course node with the Onyx test.
     * @return A map with all outcome-variables with name as key and type as value.
     */
    //<OLATCE-1012> split the method
    // <OLATBPS-363>
    public Map<String, String> getPossibleOutcomeVariables(CourseNode node) throws OnyxReporterException {
        // </OLATBPS-363>
        RepositoryEntry repositoryEntry = node.getReferencedRepositoryEntry();
        return this.getPossibleOutcomeVariables(repositoryEntry);
    }
    //<OLATCE-1012>

    //<OLATCE-1012>
    /**
     * Delivers a map with all possible outcome-variables of this onyx test.
     * @param  Repoentry with the Onyx test.
     * @return A map with all outcome-variables with name as key and type as value.
     */
    public Map<String, String> getPossibleOutcomeVariables(RepositoryEntry entry) throws OnyxReporterException {
        OnyxReporterServices reporterService = connector.getService();

        HashMapWrapper resultVariables = reporterService.getResultVariables(1, getContentPackage(entry),
                new HashMapWrapper());
        HashMap<String, String> results;
        try {
            results = resultVariables.getMap();
        } catch (OnyxReporterException e) {
            log.error("Error in getPossibleOutcomeVariables reporter conversation! RepositoryEntry: "
                    + entry.getResourceableId(), e);
            results = new HashMap<String, String>();
        }

        return results;
    }
    //<OLATCE-1012>   

    // <OLATBPS-363>
    public Map<String, String> getResults(File resultXml, CourseNode node) throws OnyxReporterException {
        // </OLATBPS-363>
        return getResults(resultXml, node, null);
    }

    public Map<String, String> getResults(AssessableCourseNode node, Identity identity)
            throws OnyxReporterException {
        //<OLATCE-1073>
        File resultXml = getResultFile(identity.getName(),
                node.getModuleConfiguration().get(IQEditController.CONFIG_KEY_TYPE).toString(), node, 0);
        return getResults(resultXml, node, identity);
        //</OLATCE-1073>
    }

    // <OLATBPS-363>
    public Map<String, String> getResults(File resultXml, CourseNode node, Identity identity)
            throws OnyxReporterException {
        // </OLATBPS-363>
        //<OLATCE-1073>
        if (resultXml == null) {
            log.info("Missing resultFile! For " + (identity != null ? identity.getName() : "NULL") + " node : "
                    + (node != null ? (node.getShortName() + ":" + node.getIdent()) : "NULL"));
            return new HashMap<String, String>(0);
        }
        //</OLATCE-1073>

        RepositoryEntry repositoryEntry = node.getReferencedRepositoryEntry();
        OnyxReporterServices reporterService = connector.getService();

        /** ARM SITE **/
        String[] dlh = armSite(reporterService, identity, ReporterRole.AUTHOR);

        String secret = dlh[0];
        String sessionId = dlh[1];

        /** Prepare data **/
        ArrayList<ResultsForStudent> resForStudents = new ArrayList<ResultsForStudent>(4);

        resForStudents.add(getStudentWithResult(identity, resultXml));

        ResultsForStudentsWrapper wrapper = new ResultsForStudentsWrapper();

        wrapper.setStudents(resForStudents);

        /** INITIATE SITE **/
        reporterService.initiateSite(1, sessionId, secret, wrapper, getContentPackage(repositoryEntry),
                new HashMapWrapper());

        HashMapWrapper mapWrapper = reporterService.getResultValues(1, sessionId, secret, new HashMapWrapper(),
                new HashMapWrapper());
        Map<String, String> results;
        try {
            results = mapWrapper.getMap();
        } catch (OnyxReporterException e) {
            log.error("Error in getResults reporter conversation! Session: " + sessionId + ", Identity: "
                    + identity.getName(), e);
            throw new OnyxReporterException("Error getting results for test! Session: " + sessionId, e);
        }

        return results;
    }

    /**
     * This method starts the OnyxReporter and returns the link to it.
     * 
     * @param students
     *            The students to show the results for.
     * @param node
     *            The AssessableCourseNode to get the nodeId and to get the
     *            (OnyxTest) RepositoryEntry.
     * @param role
     *            defines for which role and which resulting view the reporter
     *            should be called
     * @param ureq
     *            The UserRequest for getting the identity and role of the
     *            current user.
     * @return the Link to the reporter.
     */
    public String startReporterGUI(Identity caller, List<Identity> students, CourseNode node, Long assessmentId,
            ReporterRole role) throws OnyxReporterException {

        String link = "";
        RepositoryEntry repositoryEntry = node.getReferencedRepositoryEntry();

        ArrayList<ResultsForStudent> resForStudents = null;
        if (surveyFolderPath == null) {
            resForStudents = getStudentsWithResults(students, node, assessmentId);
        } else {
            resForStudents = getAnonymizedStudentsWithResultsForSurvey(node.getIdent());
        }

        try {
            OnyxReporterServices reporterService = connector.getService();

            HashMapWrapper mapWrapper = new HashMapWrapper();
            if (ReporterRole.ASSESSMENT == role) {
                HashMap<String, String> underlyingMap = new HashMap<String, String>();
                if (assessmentId != null) {
                    underlyingMap.put("assessmentID", String.valueOf(assessmentId));
                }
                underlyingMap.put("providerID", OnyxModule.getConfigName());
                mapWrapper.setMap(underlyingMap);
            }

            String[] dlh = armSite(reporterService, caller, role, mapWrapper);

            byte[] contentPackage = getContentPackage(repositoryEntry);

            ResultsForStudentsWrapper wrapper = new ResultsForStudentsWrapper();
            wrapper.setStudents(resForStudents);

            link = reporterService.initiateSite(1, dlh[1], dlh[0], wrapper, contentPackage, mapWrapper);

            if (link == null) {
                throw new OnyxReporterException("Unable to start ReporterGUI! Could not resolve reporter URL!");
            } else if (link.indexOf("reportererror") >= 0) {
                // use error link to show reporter error page
            } else {
                //<OLATCE-1124>
                if (ReporterRole.REPORTING == role) {
                    link += FIVE; // view 5 for reporting view / statistical evaluation
                } else if (ReporterRole.STUDENT == role) {
                    link += ONE; // view 1 (single learner view)
                } else {
                    link += FOUR; // view 4 (all learners overview)
                }
                //</OLATCE-1124>

                //add params
                link += "?sid=" + dlh[1] + "&secret=" + dlh[0];

                //switch to the student view of a specified student
                if (ReporterRole.STUDENT == role) {
                    //link += "&uid="+ students.get(0).getKey();
                    link += "&uid=" + assessmentId;
                }

                // add language information
                final String lang = caller.getUser().getPreferences().getLanguage();
                if (lang != null && !lang.isEmpty()) {
                    link += "&lang=" + lang;
                }
            }
        } catch (Exception e) {
            throw new OnyxReporterException("Unable to start ReporterGUI!", e);
        }

        return link;
    }

    // <OLATCE-498>
    public boolean hasResults(String username, String assessmentType, CourseNode node) {
        return getResultFile(username, assessmentType, node, 0) != null;
    }
    // </OLATCE-498>

    public String startReporterGUIForSurvey(Identity caller, CourseNode node, String resultsPath)
            throws OnyxReporterException {
        this.surveyFolderPath = resultsPath;
        //<OLATCE-1124>
        return startReporterGUI(caller, null, node, null, ReporterRole.REPORTING);
        //</OLATCE-1124>
    }

    private byte[] getContentPackage(RepositoryEntry repositoryEntry) {
        File cpFile = FileResourceManager.getInstance().getFileResource(repositoryEntry.getOlatResource());

        if (cpFile == null || !cpFile.exists()) {
            cpFile = getCP(repositoryEntry);
        }

        Long fileLength = cpFile.length();
        byte[] contentPackage = new byte[fileLength.intValue()];

        java.io.FileInputStream inp = null;
        try {
            inp = new java.io.FileInputStream(cpFile);
            inp.read(contentPackage);
        } catch (FileNotFoundException e) {
            log.error("Missing file: " + cpFile.getAbsolutePath(), e);
        } catch (IOException e) {
            log.error("Error copying file: " + cpFile.getAbsolutePath(), e);
        } finally {
            IOUtils.closeQuietly(inp);
        }

        return contentPackage;
    }

    private String[] armSite(OnyxReporterServices reporterService, Identity caller, ReporterRole role) {
        return armSite(reporterService, caller, role, new HashMapWrapper());
    }

    private String[] armSite(OnyxReporterServices reporterService, Identity caller, ReporterRole role,
            HashMapWrapper wrapper) {

        String secret = "" + new Random().nextLong();

        //<OLATCE-1089>
        String lastname = caller.getUser().getProperty(UserConstants.LASTNAME, null);
        String firstname = caller.getUser().getProperty(UserConstants.FIRSTNAME, null);
        lastname = lastname != null && lastname.length() > 0 ? lastname : NONAME;
        firstname = firstname != null && firstname.length() > 0 ? firstname : NONAME;

        String reporterSessionId = reporterService.armSite(1, caller.getName(), role.getKey(), secret, lastname,
                firstname, wrapper);
        //</OLATCE-1089>
        return new String[] { secret, reporterSessionId != null ? reporterSessionId : "dummy" };
    }

    private ResultsForStudent getStudentWithResult(Identity student, File resultFile) {
        ResultsForStudent resForStudent = null;
        Long fileLength = resultFile.length();
        byte[] resultFileStream = new byte[fileLength.intValue()];
        java.io.FileInputStream inp = null;

        try {
            inp = new java.io.FileInputStream(resultFile);
            inp.read(resultFileStream);

            //<OLATCE-1089>
            String lastname = student.getUser().getProperty(UserConstants.LASTNAME, null);
            String firstname = student.getUser().getProperty(UserConstants.FIRSTNAME, null);
            lastname = lastname != null && lastname.length() > 0 ? lastname : NONAME;
            firstname = firstname != null && firstname.length() > 0 ? firstname : NONAME;

            resForStudent = new ResultsForStudent();
            //<OLATCE-1169>
            resForStudent.setFirstname(firstname);
            resForStudent.setLastname(lastname);
            //</OLATCE-1169>
            //</OLATCE-1089>
            //resForStudent.setStudentId(String.valueOf(student.getKey()));
            String filename = resultFile.getName();
            Matcher matcher = pattern.matcher(filename);
            String assessmentId = null;
            if (matcher.matches()) {
                assessmentId = matcher.group(2);
            } else {
                final Long key = student.getKey();
                log.warn("Could not determine assessment ID from unexpected file name " + filename);
                assessmentId = String.valueOf(key);
            }
            resForStudent.setStudentId(assessmentId);

            resForStudent.setGroupname("");
            resForStudent.setTutorname("");
            resForStudent.setResultsFile(resultFileStream);

        } catch (FileNotFoundException e) {
            log.error("Missing file: " + resultFile.getAbsolutePath(), e);
        } catch (IOException e) {
            log.error("Error copying file: " + resultFile.getAbsolutePath(), e);
        } finally {
            IOUtils.closeQuietly(inp);
        }
        return resForStudent;

    }

    private ArrayList<ResultsForStudent> getStudentsWithResults(List<Identity> students, CourseNode node,
            Long assessmentId) {
        ArrayList<ResultsForStudent> resForStudents = new ArrayList<ResultsForStudent>();

        for (Identity student : students) {
            File resultFile = getResultFile(student.getName(),
                    node.getModuleConfiguration().get(IQEditController.CONFIG_KEY_TYPE).toString(), node,
                    assessmentId != null ? assessmentId : 0);
            //<OLATCE-1048>
            if (resultFile != null) {
                resForStudents.add(getStudentWithResult(student, resultFile));
            }
            //</OLATCE-1048>
        }

        return resForStudents;
    }

    /**
     * For every result xml file found in the survey folder a dummy student is created.
     * @param nodeId
     * @return
     */
    private ArrayList<ResultsForStudent> getAnonymizedStudentsWithResultsForSurvey(String nodeId) {
        ArrayList<ResultsForStudent> serviceStudents = new ArrayList<ResultsForStudent>();

        File directory = new File(this.surveyFolderPath);
        Long fileLength;
        File resultFile;
        if (directory.exists()) {
            String[] allXmls = directory.list(new OnyxReporterConnectorFileNameFilter(nodeId));
            if (allXmls != null && allXmls.length > 0) {
                int id = 0;
                for (String xmlFileName : allXmls) {

                    ResultsForStudent serviceStudent = new ResultsForStudent();
                    serviceStudent.setFirstname("");
                    serviceStudent.setLastname("");
                    serviceStudent.setGroupname("");
                    serviceStudent.setTutorname("");
                    serviceStudent.setStudentId("st" + id);

                    resultFile = new File(this.surveyFolderPath + xmlFileName);
                    fileLength = resultFile.length();
                    byte[] resultFileStream = new byte[fileLength.intValue()];
                    java.io.FileInputStream inp;

                    try {
                        inp = new java.io.FileInputStream(resultFile);
                        inp.read(resultFileStream);

                        serviceStudent.setResultsFile(resultFileStream);

                        serviceStudents.add(serviceStudent);
                        id++;
                    } catch (FileNotFoundException e) {
                        log.error("Missing file: " + resultFile.getAbsolutePath(), e);
                    } catch (IOException e) {
                        log.error("Error copying file: " + resultFile.getAbsolutePath(), e);
                    }
                }
            }
        }
        return serviceStudents;
    }

    public static String getFilePath(String username, String assessmentType) {
        new File(WebappHelper.getUserDataRoot());
        return OnyxResultManager.getResReporting() + File.separator + username + File.separator + assessmentType
                + File.separator;
    }

    public static File getResultFileOrNull(QTIResultSet set, CourseNode node) {
        if (set == null || node == null) {
            return null;
        }
        return getResultFileOrNull(set.getIdentity().getName(),
                node.getModuleConfiguration().get(IQEditController.CONFIG_KEY_TYPE).toString(), node,
                set.getAssessmentID());
    }

    private static File getResultFileOrNull(String username, String assessmentType, CourseNode node,
            long assessmentId) {
        File xml = null;
        String prefix;
        String path = getFilePath(username, assessmentType);
        //if an assessment id was given, use the corresponding file
        if (assessmentId != 0) {
            prefix = OnyxResultManager.getResultsFilenamePrefix(path, node, assessmentId);
            xml = new File(WebappHelper.getUserDataRoot(), prefix + OnyxResultManager.SUFFIX_ZIP);
            if (xml.exists()) {
                return xml;
            }
            // fall back to "old" xml implementation
            xml = new File(WebappHelper.getUserDataRoot(), prefix + OnyxResultManager.SUFFIX_XML);
        }
        return xml;
    }

    private File getResultFile(String username, String assessmentType, CourseNode node, long assessmentId) {
        File fUserdataRoot = new File(WebappHelper.getUserDataRoot());
        String path = getFilePath(username, assessmentType);
        File xml = getResultFileOrNull(username, assessmentType, node, assessmentId);
        //otherwise search the newest result file with this node id in this directory
        if (xml == null || !(xml.exists())) {
            File directory = new File(fUserdataRoot, path);
            String[] allXmls = directory.list(new OnyxReporterConnectorFileNameFilter(node.getIdent()));
            if (allXmls != null && allXmls.length > 0) {
                File newestXml = new File(fUserdataRoot, path + allXmls[0]);
                File newestZip = null;
                /*
                 * Search for newest file in array. If ZIP files are found,
                 * prefer them. Use XML files otherwise.
                 */
                for (String xmlFileName : allXmls) {
                    File xmlFile = new File(fUserdataRoot, path + xmlFileName);
                    Matcher matcher = pattern.matcher(xmlFileName);
                    String currentAssessmentId = null;
                    if (matcher.matches()) {
                        currentAssessmentId = matcher.group(2);
                        QTIResultSet resultSet = OnyxResultManager
                                .getResultSet(Long.parseLong(currentAssessmentId));
                        if (resultSet != null && !resultSet.getSuspended()) {
                            if (xmlFileName.endsWith(OnyxResultManager.SUFFIX_ZIP)) {
                                if (newestZip == null) {
                                    newestZip = xmlFile;
                                } else {
                                    if (xmlFile.lastModified() > newestZip.lastModified()) {
                                        newestZip = xmlFile;
                                    }
                                }
                            } else if (xmlFile.lastModified() > newestXml.lastModified()) {
                                newestXml = xmlFile;
                            }
                        } else {
                            log.info("Skip suspended result : " + xmlFile);
                        }
                    }
                }
                if (newestZip != null) {
                    xml = newestZip;
                } else {
                    xml = newestXml;
                }
            }
        }

        if (xml == null || !(xml.exists())) {
            //<OLATCE-1048>
            xml = null;
            //</OLATCE-1048>
            //log.error("There is no file for this test and student "+username+" assessmentType: "+assessmentType+ " nodeId: "+nodeId+" assessmentId: "+assessmentId);
        }

        return xml;
    }

    /**
     * Generates a file object for the given re.
     * @param repositoryEntry
     * @return
     */
    private File getCP(RepositoryEntry repositoryEntry) {
        //get content-package (= onyx test zip-file)
        OLATResourceable fileResource = repositoryEntry.getOlatResource();
        String unzipedDir = FileResourceManager.getInstance().unzipFileResource(fileResource).getAbsolutePath();
        String zipdirName = FileResourceManager.ZIPDIR;
        String testName = repositoryEntry.getResourcename();
        String pathToFile = unzipedDir.substring(0, unzipedDir.indexOf(zipdirName));
        File onyxTestZip = new File(pathToFile + testName);
        // <OLATCE-499>
        if (!onyxTestZip.exists()) {
            onyxTestZip = new File(pathToFile + "repo.zip");
        }
        // </OLATCE-499>
        return onyxTestZip;
    }

    private boolean isServiceAvailable(String target) {

        HostnameVerifier hv = new HostnameVerifier() {
            @Override
            public boolean verify(String urlHostName, SSLSession session) {
                if (urlHostName.equals(session.getPeerHost())) {
                    return true;
                } else {
                    return false;
                }
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);

        try {
            URL url = new URL(target + "?wsdl");
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            if (con instanceof HttpsURLConnection) {
                HttpsURLConnection sslconn = (HttpsURLConnection) con;
                SSLContext context = SSLContext.getInstance("SSL");
                context.init(SSLConfigurationModule.getKeyManagers(), SSLConfigurationModule.getTrustManagers(),
                        new java.security.SecureRandom());
                sslconn.setSSLSocketFactory(context.getSocketFactory());
                sslconn.connect();
                if (sslconn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    sslconn.disconnect();
                    return true;
                }
            } else {
                con.connect();
                if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    con.disconnect();
                    return true;
                }
            }
        } catch (Exception e) {
            Tracing.createLoggerFor(getClass()).error("Error while trying to connect to webservice: " + target, e);
        }
        return false;
    }

    //<OLATCE-1124>
    public boolean hasAnyResults(boolean forSurvey, List<Identity> forStudents, String surveyFolder,
            CourseNode node) {
        boolean hasResults = false;

        FilenameFilter filter = new OnyxReporterConnectorFileNameFilter(node.getIdent());
        File directory = new File(WebappHelper.getUserDataRoot());
        if (forSurvey) {
            File surveyDir = new File(surveyFolder);
            if (surveyDir.exists()) {

                String[] allXmls = surveyDir.list(filter);
                if (allXmls != null && allXmls.length > 0) {
                    hasResults = true;
                }
            }
        } else {
            String assessmentType = node.getModuleConfiguration().get(IQEditController.CONFIG_KEY_TYPE).toString();
            String path = null;
            for (Identity student : forStudents) {
                path = OnyxResultManager.getResReporting() + File.separator + student.getName() + File.separator
                        + assessmentType + File.separator;
                File xml = new File(directory, path);

                if (xml != null && xml.exists()) {
                    String[] allXmls = xml.list(filter);
                    if (allXmls != null && allXmls.length > 0) {
                        hasResults = true;
                        break;
                    }
                }

            }
        }

        return hasResults;
    }
    //</OLATCE-1124>
}
//</ONYX-705>