package org.societies.webapp.controller;
/**
* Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET
* (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije
* informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE
* COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp.,
* INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM
* ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC))
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.DefaultCategoryDataset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.societies.api.identity.IIdentity;
import org.societies.api.internal.privacytrust.privacyprotection.model.privacyassessment.AssessmentResultClassName;
import org.societies.api.internal.privacytrust.privacyprotection.model.privacyassessment.AssessmentResultIIdentity;
import org.societies.api.internal.privacytrust.privacyprotection.model.privacyassessment.IAssessment;
import org.societies.webapp.models.PrivacyAssessmentForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.io.File;
import java.io.IOException;
import java.util.*;
@Controller
public class PrivacyAssessmentController {
private static Logger LOG = LoggerFactory.getLogger(PrivacyAssessmentController.class);
private static final String RESULT = "result";
// FIXME: The path should not depend on Virgo version, etc.
private static final String contextPath = "work/org.eclipse.virgo.kernel.deployer_3.0.2.RELEASE/staging/" +
"global/bundle/societies-webapp/0.4.1/societies-webapp.war/";
/**
* URL parts without prefix and suffix
*/
public class PageNames {
public static final String PRIVACY_ASSESSMENT = "privacy-assessment";
public static final String PRIVACY_ASSESSMENT_SETTINGS = "privacy-assessment-settings";
public static final String PRIVACY_ASSESSMENT_CHART = "privacy-assessment-chart";
public static final String PRIVACY_ASSESSMENT_TABLE = "privacy-assessment-table";
}
/**
* Parameters for presentation of results. These affect the view.
*/
public class Presentation {
/**
* Types or categories of subjects of privacy assessment to be shown
*/
public class SubjectTypes {
// Values
public static final String RECEIVER_IDS = "Receiver identities";
public static final String SENDER_IDS = "Sender identities";
public static final String SENDER_CLASSES = "Sender classes";
public static final String DATA_ACCESS_IDS = "Data access by identities";
public static final String DATA_ACCESS_CLASSES = "Data access by classes";
// Keys
public static final String RECEIVER_IDS_KEY = "receiverIds";
public static final String SENDER_IDS_KEY = "senderIds";
public static final String SENDER_CLASSES_KEY = "senderClasses";
public static final String DATA_ACCESS_IDS_KEY = "dataAccessIds";
public static final String DATA_ACCESS_CLASSES_KEY = "dataAccessClasses";
}
/**
* Subjects of privacy assessment to be shown
*/
public class Subjects {
// Keys and values
public static final String ALL = "All";
}
/**
* Format of presentation
*/
public class Format {
// Keys and values
public static final String CHART = "Chart";
public static final String TABLE = "Table";
}
}
public class PlotData {
private double[] data;
private String[] labels;
public PlotData(double[] data, Object[] labels) {
this.data = data;
this.labels = obj2str(labels);
}
private String[] obj2str(Object[] obj) {
Class<? extends Object> clazz;
if (obj.length == 0) {
return new String[0];
}
clazz = obj[0].getClass();
if (String.class.isAssignableFrom(clazz)) {
//return (String[]) obj;
String[] labelsStr = new String[obj.length];
for (int k = 0; k < obj.length; k++) {
LOG.debug("obj2str: obj[{}] = {}", k, obj[k]);
labelsStr[k] = (String) obj[k];
}
return labelsStr;
} else if (IIdentity.class.isAssignableFrom(clazz)) {
String[] labelsStr = new String[obj.length];
for (int k = 0; k < obj.length; k++) {
LOG.debug("obj2str: obj[{}] = {}", k, obj[k]);
labelsStr[k] = obj[k] == null ? null : ((IIdentity) obj[k]).getJid();
}
return labelsStr;
} else {
LOG.warn("Unsupported class: {}", clazz.getName());
return new String[0];
}
}
public double[] getData() {
for (double d : data) {
LOG.debug("getData(): {}", d);
}
return data;
}
public String[] getLabels() {
for (String s : labels) {
LOG.debug("getLabels(): {}", s);
}
return labels;
}
}
/**
* OSGI service get auto injected
*/
@Autowired
private IAssessment assessment;
public IAssessment getAssessment() {
return assessment;
}
public void setAssessment(IAssessment sdService) {
LOG.debug("setAssessment()");
this.assessment = sdService;
}
@RequestMapping(value = "/" + PageNames.PRIVACY_ASSESSMENT + ".html", method = RequestMethod.GET)
public ModelAndView privacyAssessment() {
LOG.debug(PageNames.PRIVACY_ASSESSMENT + " HTTP GET");
//CREATE A HASHMAP OF ALL OBJECTS REQUIRED TO PROCESS THIS PAGE
Map<String, Object> model = new HashMap<String, Object>();
model.put("message", "Please input values and submit");
//ADD THE BEAN THAT CONTAINS ALL THE FORM DATA FOR THIS PAGE
PrivacyAssessmentForm assForm = new PrivacyAssessmentForm();
model.put("assForm", assForm);
//ADD ALL THE SELECT BOX VALUES USED ON THE FORM
Map<String, String> assessmentSubjectTypes = new LinkedHashMap<String, String>();
assessmentSubjectTypes.put(Presentation.SubjectTypes.RECEIVER_IDS_KEY, Presentation.SubjectTypes.RECEIVER_IDS);
assessmentSubjectTypes.put(Presentation.SubjectTypes.SENDER_IDS_KEY, Presentation.SubjectTypes.SENDER_IDS);
assessmentSubjectTypes.put(Presentation.SubjectTypes.SENDER_CLASSES_KEY, Presentation.SubjectTypes.SENDER_CLASSES);
assessmentSubjectTypes.put(Presentation.SubjectTypes.DATA_ACCESS_IDS_KEY, Presentation.SubjectTypes.DATA_ACCESS_IDS);
assessmentSubjectTypes.put(Presentation.SubjectTypes.DATA_ACCESS_CLASSES_KEY, Presentation.SubjectTypes.DATA_ACCESS_CLASSES);
model.put("assessmentSubjectTypes", assessmentSubjectTypes);
Map<String, String> presentationFormats = new LinkedHashMap<String, String>();
presentationFormats.put(Presentation.Format.CHART, Presentation.Format.CHART);
presentationFormats.put(Presentation.Format.TABLE, Presentation.Format.TABLE);
model.put("presentationFormats", presentationFormats);
Map<String, String> assessmentSubjects = new LinkedHashMap<String, String>();
assessmentSubjects.put(Presentation.Subjects.ALL, Presentation.Subjects.ALL);
model.put("assessmentSubjects", assessmentSubjects);
return new ModelAndView(PageNames.PRIVACY_ASSESSMENT, model);
}
@SuppressWarnings("unchecked")
@RequestMapping(value = "/" + PageNames.PRIVACY_ASSESSMENT + ".html", method = RequestMethod.POST)
public ModelAndView privacyAssessment(@Valid PrivacyAssessmentForm assForm,
BindingResult result, Map model) {
LOG.debug(PageNames.PRIVACY_ASSESSMENT + " HTTP POST");
if (result.hasErrors()) {
LOG.warn("BindingResult has errors");
model.put(RESULT, "privacy assessment form error");
return new ModelAndView(PageNames.PRIVACY_ASSESSMENT, model);
}
if (assessment == null) {
LOG.warn("Privacy Assessment Service reference not avaiable");
model.put("errormsg", "Privacy Assessment Service reference not avaiable");
return new ModelAndView("error", model);
}
String presentationFormat = assForm.getPresentationFormat();
String subjectType = assForm.getAssessmentSubjectType();
LOG.debug("presentationFormat = {}, subjectType = {}", presentationFormat, subjectType);
Collection<PrivacyAssessmentForm> charts = new ArrayList<PrivacyAssessmentForm>();
if (presentationFormat.equalsIgnoreCase(Presentation.Format.CHART)) {
String chartFileName = "chart-1.png";
String title;
String xlabel;
String ylabel;
PlotData[] plotData;
String[] plotDataLabels;
if (subjectType.equalsIgnoreCase(Presentation.SubjectTypes.RECEIVER_IDS_KEY)) {
title = Presentation.SubjectTypes.RECEIVER_IDS;
xlabel = "Receiver identity";
ylabel = "Number of data transmissions";
Map<IIdentity, Integer> identities;
identities = assessment.getNumDataTransmissionEventsForAllReceivers(
new Date(0), new Date());
LOG.debug("Number of identities data has been transmitted to: {}", identities.size());
plotData = new PlotData[]{mapToArrays(identities)};
plotDataLabels = new String[]{"data"};
} else if (subjectType.equalsIgnoreCase(Presentation.SubjectTypes.SENDER_IDS_KEY)) {
title = Presentation.SubjectTypes.SENDER_IDS;
xlabel = "Sender identity";
ylabel = "Correlation of data transmission and data access";
HashMap<IIdentity, AssessmentResultIIdentity> assResult;
assResult = assessment.getAssessmentAllIds(null, null);
int size = assResult.size();
IIdentity[] labels = new IIdentity[size];
double[][] data = new double[2][size];
Iterator<IIdentity> iterator = assResult.keySet().iterator();
LOG.debug("privacyAssessment(): size = {}", size);
for (int k = 0; k < size; k++) {
labels[k] = iterator.next();
data[0][k] = assResult.get(labels[k]).getCorrWithDataAccessBySender();
data[1][k] = assResult.get(labels[k]).getCorrWithDataAccessByAll();
LOG.debug("privacyAssessment(): label[{}] = {}", k, labels[k]);
LOG.debug("privacyAssessment(): data[0][{}] = {}", k, data[0][k]);
LOG.debug("privacyAssessment(): data[1][{}] = {}", k, data[1][k]);
}
plotData = new PlotData[]{
new PlotData(data[0], labels),
new PlotData(data[1], labels)
};
plotDataLabels = new String[]{
"Correlation with data access by the sender identity",
"Correlation with data access by any identity"
};
} else if (subjectType.equalsIgnoreCase(Presentation.SubjectTypes.SENDER_CLASSES_KEY)) {
title = Presentation.SubjectTypes.SENDER_CLASSES;
xlabel = "Sender class";
ylabel = "Correlation of data transmission and data access";
HashMap<String, AssessmentResultClassName> assResult;
assResult = assessment.getAssessmentAllClasses(true, null, null);
int size = assResult.size();
String[] labels = new String[size];
double[][] data = new double[2][size];
Iterator<String> iterator = assResult.keySet().iterator();
LOG.debug("privacyAssessment(): size = {}", size);
for (int k = 0; k < size; k++) {
labels[k] = iterator.next();
data[0][k] = assResult.get(labels[k]).getCorrWithDataAccessBySender();
data[1][k] = assResult.get(labels[k]).getCorrWithDataAccessByAll();
LOG.debug("privacyAssessment(): label[{}] = {}", k, labels[k]);
LOG.debug("privacyAssessment(): data[0][{}] = {}", k, data[0][k]);
LOG.debug("privacyAssessment(): data[1][{}] = {}", k, data[1][k]);
}
plotData = new PlotData[]{
new PlotData(data[0], labels),
new PlotData(data[1], labels)
};
plotDataLabels = new String[]{
"Correlation with data access by the sender class",
"Correlation with data access by any class"
};
} else if (subjectType.equalsIgnoreCase(Presentation.SubjectTypes.DATA_ACCESS_CLASSES_KEY)) {
title = Presentation.SubjectTypes.DATA_ACCESS_CLASSES;
xlabel = "Class";
ylabel = "Number of accesses to local data";
Map<String, Integer> dataAccessClasses;
dataAccessClasses = assessment.getNumDataAccessEventsForAllClasses(true, new Date(0), new Date());
LOG.debug("Number of data access events (by class): {}", dataAccessClasses.size());
plotData = new PlotData[]{mapToArrays(dataAccessClasses)};
plotDataLabels = new String[]{"data"};
} else if (subjectType.equalsIgnoreCase(Presentation.SubjectTypes.DATA_ACCESS_IDS_KEY)) {
title = Presentation.SubjectTypes.DATA_ACCESS_IDS;
xlabel = "Identity";
ylabel = "Number of accesses to local data";
Map<IIdentity, Integer> identities;
identities = assessment.getNumDataAccessEventsForAllIdentities(new Date(0), new Date());
LOG.debug("Number of data access events (by identity): {}", identities.size());
plotData = new PlotData[]{mapToArrays(identities)};
plotDataLabels = new String[]{"data"};
} else {
LOG.warn("Unexpected {}: {}", Presentation.SubjectTypes.class.getSimpleName(), subjectType);
return privacyAssessment();
}
PrivacyAssessmentForm form1 = new PrivacyAssessmentForm();
form1.setAssessmentSubject(title);
// createBarchart(null, xlabel, ylabel, dataLabels, data, chartFileName);
createBarchart(null, xlabel, ylabel, plotData, plotDataLabels, chartFileName);
form1.setChart(chartFileName);
charts.add(form1);
// PrivacyAssessmentForm form2 = new PrivacyAssessmentForm();
// form2.setAssessmentSubject(title + " 2");
// form2.setChart(chartFileName);
// charts.add(form2);
model.put("assessmentResults", charts);
LOG.debug(PageNames.PRIVACY_ASSESSMENT + " HTTP POST end");
return new ModelAndView(PageNames.PRIVACY_ASSESSMENT_CHART, model);
} else if (presentationFormat.equalsIgnoreCase(Presentation.Format.TABLE)) {
HashMap<String, AssessmentResultClassName> assResult;
assResult = assessment.getAssessmentAllClasses(true, null, null);
model.put("assessmentResults", assResult.values());
LOG.debug(PageNames.PRIVACY_ASSESSMENT + " HTTP POST end");
return new ModelAndView(PageNames.PRIVACY_ASSESSMENT_TABLE, model);
} else {
LOG.warn("Unexpected {}: {}", Presentation.Format.class.getSimpleName(), presentationFormat);
return privacyAssessment();
}
}
/**
* @param map Map<String, Integer> or Map<IIdentity, Integer>
*/
private PlotData mapToArrays(Map map) {
Object[] labels = new Object[map.keySet().size()];
double[] data = new double[map.keySet().size()];
int k;
k = 0;
for (Object key : map.keySet()) {
labels[k] = key;
data[k] = (Integer) map.get(key);
++k;
}
return new PlotData(data, labels);
}
// private void createBarchart(String title, String categoryLabel, String valueLabel,
// PlotData data, String filename) {
//
// createBarchart(title, categoryLabel, valueLabel, new PlotData[] {data},
// new String[] {"data"}, filename);
// }
private void createBarchart(String title, String categoryLabel, String valueLabel,
PlotData[] data, String[] dataLabels, String filename) {
DefaultCategoryDataset dataSet = new DefaultCategoryDataset();
int maxDataLength = 0;
for (int i = 0; i < dataLabels.length; i++) {
for (int j = 0; j < data[i].getData().length; j++) {
dataSet.addValue(data[i].getData()[j], dataLabels[i], data[i].getLabels()[j]);
}
if (maxDataLength < data[i].getData().length) {
maxDataLength = data[i].getData().length;
}
}
JFreeChart chart = ChartFactory.createBarChart(
title,
categoryLabel,
valueLabel,
dataSet,
PlotOrientation.VERTICAL,
dataLabels.length > 1, // display legend
false,
false
);
CategoryPlot plot = (CategoryPlot) chart.getPlot();
CategoryAxis xAxis = (CategoryAxis) plot.getDomainAxis();
xAxis.setCategoryLabelPositions(CategoryLabelPositions.STANDARD);
xAxis.setMaximumCategoryLabelLines(6);
//chart.setBackgroundPaint(ChartColor.WHITE);
int width = 50 + maxDataLength * 170;
if (maxDataLength < 3) {
width += 170;
}
int height = 400;
try {
File file = new File(contextPath + filename);
ChartUtilities.saveChartAsPNG(file, chart, width, height);
} catch (IOException e) {
LOG.warn("createBarchart(): ", e);
}
}
@RequestMapping(value = "/" + PageNames.PRIVACY_ASSESSMENT_SETTINGS + ".html", method = RequestMethod.GET)
public ModelAndView privacyAssessmentSettings() {
LOG.debug(PageNames.PRIVACY_ASSESSMENT_SETTINGS + " HTTP GET");
//CREATE A HASHMAP OF ALL OBJECTS REQUIRED TO PROCESS THIS PAGE
Map<String, Object> model = new HashMap<String, Object>();
model.put("message", "Please input values and submit");
//ADD THE BEAN THAT CONTAINS ALL THE FORM DATA FOR THIS PAGE
PrivacyAssessmentForm assForm = new PrivacyAssessmentForm();
assForm.setAssessNow(false);
//int autoReassessmentInSecs = assessment.getAutoPeriod();
int autoReassessmentInSecs = 0; // FIXME: remove this obsolete functionality
LOG.warn("Automatic periodical assessment is no longer implemented.");
assForm.setAutoReassessment(autoReassessmentInSecs >= 0);
assForm.setAutoReassessmentInSecs(autoReassessmentInSecs);
model.put("assForm", assForm);
return new ModelAndView(PageNames.PRIVACY_ASSESSMENT_SETTINGS, model);
}
@SuppressWarnings("unchecked")
@RequestMapping(value = "/" + PageNames.PRIVACY_ASSESSMENT_SETTINGS + ".html", method = RequestMethod.POST)
public ModelAndView privacyAssessmentSettings(@Valid PrivacyAssessmentForm assForm,
BindingResult result, Map model) {
LOG.debug(PageNames.PRIVACY_ASSESSMENT_SETTINGS + " HTTP POST");
if (result.hasErrors()) {
LOG.warn("BindingResult has errors");
model.put(RESULT, "privacy assessment form error");
return new ModelAndView("error", model);
}
if (assessment == null) {
LOG.warn("Privacy Assessment Service reference not avaiable");
model.put("errormsg", "Privacy Assessment Service reference not avaiable");
return new ModelAndView("error", model);
}
int autoAssessmentPeriod = assForm.getAutoReassessmentInSecs();
boolean assessNow = assForm.isAssessNow();
LOG.debug("autoReassessmentInSecs = {}, assessNow = {}", autoAssessmentPeriod, assessNow);
try {
if (assessNow) {
assessment.assessAllNow(null, null);
}
if (!assForm.isAutoReassessment()) {
autoAssessmentPeriod = -1;
}
// FIXME: remove this obsolete functionality
//assessment.setAutoPeriod(autoAssessmentPeriod);
} catch (Exception ex) {
LOG.warn("", ex);
}
LOG.debug(PageNames.PRIVACY_ASSESSMENT_SETTINGS + " HTTP POST end");
return privacyAssessment();
}
}