lp-collaborative-workspace/lp-cw-bridge/lp-cw-bridge-implementation/src/main/java/eu/learnpad/cw/internal/CWXwikiBridge.java
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package eu.learnpad.cw.internal;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.ws.rs.Path;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.phase.Initializable;
import org.xwiki.component.phase.InitializationException;
import org.xwiki.configuration.ConfigurationSource;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.query.Query;
import org.xwiki.query.QueryException;
import org.xwiki.query.QueryFilter;
import org.xwiki.query.QueryManager;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;
import eu.learnpad.configuration.LearnpadPropertiesConfigurationSource;
import eu.learnpad.core.impl.cw.XwikiBridge;
import eu.learnpad.core.impl.cw.XwikiCoreFacadeRestResource;
import eu.learnpad.core.rest.DefaultRestResource;
import eu.learnpad.core.rest.RestResource;
import eu.learnpad.cw.UICWBridge;
import eu.learnpad.cw.internal.RecommendationWebsocketServer.WebSocketMetadata;
import eu.learnpad.cw.rest.data.ScoreRecord;
import eu.learnpad.cw.rest.data.ScoreRecordCollection;
import eu.learnpad.exception.LpRestException;
import eu.learnpad.exception.impl.LpRestExceptionXWikiImpl;
import eu.learnpad.me.rest.data.ModelSetType;
import eu.learnpad.or.rest.data.NotificationActionType;
import eu.learnpad.or.rest.data.Recommendations;
import eu.learnpad.or.rest.data.ResourceType;
import eu.learnpad.rest.model.jaxb.PFResults;
import eu.learnpad.rest.model.jaxb.PFResults.Feedbacks;
import eu.learnpad.rest.model.jaxb.PFResults.Feedbacks.Feedback;
import eu.learnpad.rest.model.jaxb.PFResults.Patches;
import eu.learnpad.rest.model.jaxb.PFResults.Patches.Patch;
import eu.learnpad.rest.model.jaxb.PFResults.Patches.Patch.Artefact;
import eu.learnpad.rest.model.jaxb.PFResults.Patches.Patch.Artefact.Attribute;
import eu.learnpad.rest.model.jaxb.PatchType;
import eu.learnpad.sim.rest.data.ProcessInstanceData;
import eu.learnpad.sim.rest.data.UserData;
import eu.learnpad.sim.rest.data.UserDataCollection;
@Component
@Singleton
@Named("eu.learnpad.cw.internal.CWXwikiBridge")
@Path("/learnpad/cw/bridge")
public class CWXwikiBridge extends XwikiBridge implements Initializable,
UICWBridge {
private final String LEARNPAD_SPACE = "LPCode";
private final String FEEDBACK_CLASS_PAGE = "FeedbackClass";
private final String FEEDBACK_CLASS = String.format("%s.%s",
LEARNPAD_SPACE, FEEDBACK_CLASS_PAGE);
private final String BASEELEMENT_CLASS_PAGE = "BaseElementClass";
private final String BASEELEMENT_CLASS = String.format("%s.%s",
LEARNPAD_SPACE, BASEELEMENT_CLASS_PAGE);
private final String XWIKI_SPACE = "XWiki";
private final String USER_CLASS_PAGE = "XWikiUsers";
private final String USER_CLASS = String.format("%s.%s", XWIKI_SPACE,
USER_CLASS_PAGE);
private static final Map<String, Map<String, ScoreRecord>> scoresBySessionByUser = new ConcurrentHashMap<String, Map<String, ScoreRecord>>();
@Inject
@Named("default")
RestResource restResource;
@Inject
@Named("learnpadproperties")
ConfigurationSource configurationSource;
private Map<String, Long> bannedSimIDMap;
private final long BANNING_PERIOD_IN_MILLI_SEC = 60000;
@Inject
private Logger logger;
@Inject
@Named("secure")
private QueryManager queryManager;
@Inject
@Named("unique")
private QueryFilter uniqueDocumentFilter;
@Inject
private Provider<XWikiContext> xcontextProvider;
@Inject
@Named("current")
private DocumentReferenceResolver<String> stringDocumentReferenceResolver;
@Override
public void initialize() throws InitializationException {
this.corefacade = new XwikiCoreFacadeRestResource();
this.bannedSimIDMap = Collections
.synchronizedMap(new HashMap<String, Long>());
}
@Override
public byte[] getComments(String modelSetId, String artifactId)
throws LpRestException {
// TODO Auto-generated method stub
return null;
}
@Override
public byte[] getResource(String modelSetId, String resourceId,
String linkedTo, String action) throws LpRestException {
// TODO Auto-generated method stub
return null;
}
@Override
public void modelSetImported(String modelSetId, ModelSetType type)
throws LpRestException {
// Get the model file from Core Platform
InputStream modelStream = this.corefacade.getModel(modelSetId, type);
if (modelStream == null) {
throw new LpRestExceptionXWikiImpl("Fail to get the new modelset");
}
InputStream packageStream = this.corefacade
.transform(type, modelStream);
if (packageStream == null) {
throw new LpRestExceptionXWikiImpl(
"Fail to transform the new modelset");
}
this.loadPackage(packageStream);
}
private void loadPackage(InputStream packageStream) {
// Now send the package's path to the importer for XWiki
HttpClient httpClient = restResource.getClient();
String uri = DefaultRestResource.REST_URI + "/wikis/xwiki/xff";
PostMethod postMethod = new PostMethod(uri);
postMethod.addRequestHeader("Accept", "application/octet-stream");
RequestEntity packageEntity = new InputStreamRequestEntity(
packageStream);
postMethod.setRequestEntity(packageEntity);
try {
httpClient.executeMethod(postMethod);
} catch (IOException e) {
String message = "Unable to upload the XFF package";
logger.error(message, e);
}
}
@Override
public void contentVerified(String modelSetId, String artifactId,
String resourceId, String result) throws LpRestException {
// TODO Auto-generated method stub
}
@Override
public void modelVerified(String modelSetId, String result)
throws LpRestException {
// TODO Auto-generated method stub
}
private List<Object> getFeedbacksDocuments(String modelSetId) {
String queryXWQL = String
.format("from doc.object(%s) as feedback where feedback.modelsetid = '%s'",
FEEDBACK_CLASS, modelSetId);
Query query = null;
try {
query = queryManager.createQuery(queryXWQL, Query.XWQL);
} catch (QueryException e) {
String message = String
.format("Error in building the query to gather Feedbacks in '%s' model set.",
modelSetId);
logger.error(message, e);
return null;
}
try {
return query.addFilter(uniqueDocumentFilter).execute();
} catch (QueryException e) {
String message = String
.format("Error in executing the query to gather Feedbacks in '%s' model set.",
modelSetId);
logger.error(message, e);
return null;
}
}
private Feedbacks getFeedbackList(String modelSetId) {
XWikiContext xcontext = xcontextProvider.get();
XWiki xwiki = xcontext.getWiki();
DocumentReference classReference = stringDocumentReferenceResolver
.resolve(FEEDBACK_CLASS);
List<Object> documentNames = getFeedbacksDocuments(modelSetId);
List<Feedback> feedbacksList = new ArrayList<Feedback>();
for (Object documentName : documentNames) {
DocumentReference documentReference = stringDocumentReferenceResolver
.resolve((String) documentName);
XWikiDocument document;
try {
document = xwiki.getDocument(documentReference, xcontext);
} catch (Exception e) {
String message = String
.format("Error while trying to get document '%s' to gather feedbacks on '%' model.",
documentReference.toString(), modelSetId);
logger.error(message, e);
return null;
}
BaseObject feedbackObject = document.getXObject(classReference);
Integer id = Integer.parseInt(feedbackObject.getStringValue("id"));
Integer modelId = feedbackObject.getIntValue("modelid");
Integer artefactId = feedbackObject.getIntValue("artifactid");
String content = feedbackObject.getStringValue("description");
Feedback feedback = new Feedback();
feedback.setId(id);
feedback.setModelid(modelId);
feedback.setArtefactid(artefactId);
feedback.setValue(content);
feedbacksList.add(feedback);
}
Feedbacks feedbacks = new Feedbacks();
feedbacks.setFeedback(feedbacksList);
return feedbacks;
}
private Patches getPatchList(String modelSetId) {
// TODO
Attribute attribute1 = new Attribute();
attribute1.setId("123");
attribute1.setName("name");
attribute1.setValue("adsfdsafY");
Attribute attribute2 = new Attribute();
attribute2.setId("234");
attribute2.setName("xs");
attribute2.setValue("adsfdsafY");
Attribute attribute3 = new Attribute();
attribute3.setId("345");
attribute3.setName("sd");
attribute3.setValue("adsfdsafY");
List<Attribute> attributesList = new ArrayList<Attribute>();
attributesList.add(attribute1);
attributesList.add(attribute2);
attributesList.add(attribute3);
Artefact artefact1 = new Artefact();
artefact1.setId("");
artefact1.setName("sdfds");
artefact1.setClassName("Task");
artefact1.setAttribute(attributesList);
Artefact artefact2 = new Artefact();
artefact2.setId("123");
artefact2.setName("sdfasdf");
artefact2.setClassName("Task");
artefact2.setAttribute(attributesList);
Artefact artefact3 = new Artefact();
artefact3.setId("123");
artefact3.setName("sdfasdf");
artefact3.setClassName("End Event");
Patch patch1 = new Patch();
patch1.setType(PatchType.ADD);
patch1.setId(Integer.parseInt("123"));
patch1.setModelid(Integer.parseInt("123456"));
patch1.setArtefact(artefact1);
Patch patch2 = new Patch();
patch2.setType(PatchType.EDIT);
patch2.setId(Integer.parseInt("123"));
patch2.setModelid(Integer.parseInt("123456"));
patch2.setArtefact(artefact2);
Patch patch3 = new Patch();
patch3.setType(PatchType.DELETE);
patch3.setId(Integer.parseInt("123"));
patch3.setModelid(Integer.parseInt("123456"));
patch3.setArtefact(artefact3);
List<Patch> patchesList = new ArrayList<Patch>();
patchesList.add(patch1);
patchesList.add(patch2);
patchesList.add(patch3);
Patches patches = new Patches();
patches.setPatch(patchesList);
return patches;
}
@Override
public PFResults getFeedbacks(String modelSetId) throws LpRestException {
PFResults pf = new PFResults();
pf.setFeedbacks(getFeedbackList(modelSetId));
// pf.setPatches(getPatchList(modelSetId));
pf.setPatches(new Patches());
return pf;
}
@Override
public Recommendations getRecommendations(String modelSetId,
String artifactId, String userId) throws LpRestException {
return this.corefacade.getRecommendations(modelSetId, artifactId,
userId);
}
@Override
public String getDashboardKpiDefaultViewer(String modelSetId, String userId)
throws LpRestException {
return this.corefacade.getDashboardKpiDefaultViewer(modelSetId, userId);
}
public String startDashboardKpiCalculation(String modelSetId)
throws LpRestException {
return this.corefacade.calculateKPI(modelSetId);
}
private UserDataCollection getUserProfiles(Collection<String> potentialUsers) {
XWikiContext xcontext = xcontextProvider.get();
XWiki xwiki = xcontext.getWiki();
DocumentReference userClassReference = stringDocumentReferenceResolver
.resolve(USER_CLASS);
Collection<UserData> potentialUsersCollection = new ArrayList<UserData>();
for (String userId : potentialUsers) {
UserData user = new UserData();
user.id = this.removePrefixes(userId);
DocumentReference userReference = stringDocumentReferenceResolver
.resolve(userId);
try {
XWikiDocument userDocument = xwiki.getDocument(userReference,
xcontext);
if (userDocument != null) {
BaseObject userObject = userDocument
.getXObject(userClassReference);
if (userObject != null) {
user.firstName = userObject
.getStringValue("first_name");
user.lastName = userObject.getStringValue("last_name");
user.profileURL = userDocument.getExternalURL("view",
xcontext);
Pattern pattern = Pattern.compile("(http://[^/]*/).*");
Matcher matcher = pattern.matcher(user.profileURL);
String prefix = "";
if (matcher.find()) {
prefix = matcher.group(1);
}
user.pictureURL = prefix
+ userDocument.getAttachmentURL(
userDocument.getStringValue("avatar"),
xcontext);
}
}
} catch (XWikiException e) {
String message = String
.format("Error while trying to get profile information for the user '%s'.",
userReference.toString());
logger.error(message, e);
}
potentialUsersCollection.add(user);
}
return new UserDataCollection(potentialUsersCollection);
}
@Override
public String startSimulation(String modelId, String currentUser,
Collection<String> potentialUsers) throws LpRestException {
UserDataCollection potentialUsersCollection = getUserProfiles(potentialUsers);
return this.corefacade.startSimulation(modelId, currentUser,
potentialUsersCollection);
}
@Override
public String joinSimulation(String simulationId, String userId)
throws LpRestException {
return this.corefacade.joinSimulation(simulationId, userId);
}
@Override
public Collection<String> listSimulations() throws LpRestException {
return this.corefacade.listSimulations();
}
@Override
public ProcessInstanceData getSimulationInfo(String simulationId)
throws LpRestException {
return this.corefacade.getSimulationInfo(simulationId);
}
@Override
public String getRestPrefix(String component) throws LpRestException {
return ((LearnpadPropertiesConfigurationSource) configurationSource)
.getRestPrefix(component);
}
@Override
public void notifyRecommendations(String modelsetid, String simulationid,
String userid, Recommendations recommendations)
throws LpRestException {
String xwikiUserId = String.format("XWiki.%s", userid);
if (this.isBanningPeriodExpired(simulationid)) {
for (WebSocketMetadata wsmd : RecommendationWebsocketServer.socketBox
.byUserid(xwikiUserId)) {
ObjectMapper mapper = new ObjectMapper();
String msg = "";
try {
msg = mapper.writeValueAsString(recommendations);
} catch (JsonProcessingException e) {
msg = "";
}
wsmd.timeOfLastInteraction = System.currentTimeMillis();
wsmd.socket.send(msg);
}
}
}
@Override
public void deleteRecommendations(String modelSetId, String simulationid,
String userId) throws LpRestException {
String msg = "resetting Recs for : modelSetId : " + modelSetId + "\n"
+ "simulationid : " + simulationid + "\n" + "userId : "
+ userId;
logger.info(msg);
this.bannedSimIDMap.put(simulationid, System.currentTimeMillis());
}
private boolean isBanningPeriodExpired(String simId) {
boolean status = true;
if (this.bannedSimIDMap.containsKey(simId)) {
status = (System.currentTimeMillis() > (this.bannedSimIDMap
.get(simId) + this.BANNING_PERIOD_IN_MILLI_SEC));
if (status) {
this.bannedSimIDMap.remove(simId);
}
}
return status;
}
private String removePrefixes(String userId) {
String username = userId.replaceFirst("XWiki\\.", "");
return username;
}
@Override
public void notifyScoreUpdate(ScoreRecord record) throws LpRestException {
String userid = record.getUserArtifactId();
String sessionid = record.getSessionId();
if (!scoresBySessionByUser.containsKey(userid)) {
scoresBySessionByUser.put(record.getUserArtifactId(),
new ConcurrentHashMap<String, ScoreRecord>());
}
scoresBySessionByUser.get(userid).put(sessionid, record);
}
@Override
public ScoreRecordCollection getScores(String userid, String modelid)
throws LpRestException {
Collection<ScoreRecord> res = new ArrayList<>();
for (String recordedUser : scoresBySessionByUser.keySet()) {
if (userid == null || recordedUser.equals(userid)) {
for (String session : scoresBySessionByUser.get(recordedUser)
.keySet()) {
ScoreRecord r = scoresBySessionByUser.get(recordedUser)
.get(session);
String processid = r.getProcessArtifactId();
if (modelid == null || processid.equals(modelid)) {
res.add(r);
}
}
}
}
return new ScoreRecordCollection(res);
}
@Override
public void pageNotification(String modelSetId, String modelId,
String artifactId, String resourceId, String action, String userId)
throws LpRestException {
ResourceType resourceType = ResourceType.PAGE;
this.resourceNotification(modelSetId, modelId, artifactId, resourceId,
resourceType, action, userId);
}
@Override
public void commentNotification(String modelSetId, String modelId,
String artifactId, String resourceId, String action, String userId)
throws LpRestException
{
ResourceType resourceType = ResourceType.COMMENT;
this.resourceNotification(modelSetId, modelId, artifactId, resourceId,
resourceType, action, userId);
}
@Override
public void attachmentNotification(String modelSetId, String modelId,
String artifactId, String resourceId, String action, String userId)
throws LpRestException
{
ResourceType resourceType = ResourceType.ATTACHMENT;
this.resourceNotification(modelSetId, modelId, artifactId, resourceId,
resourceType, action, userId);
}
@Override
public void feedbackNotification(String modelSetId, String modelId,
String artifactId, String resourceId, String action, String userId)
throws LpRestException {
ResourceType resourceType = ResourceType.FEEDBACK;
this.resourceNotification(modelSetId, modelId, artifactId, resourceId,
resourceType, action, userId);
}
private void resourceNotification(String modelSetId, String modelId,
String artifactId, String resourceId, ResourceType resourceType,
String action, String userId) throws LpRestException {
NotificationActionType actionType = null;
try {
actionType = NotificationActionType.valueOf(action.toUpperCase());
} catch (IllegalArgumentException | NullPointerException e) {
LpRestExceptionXWikiImpl e1 = new LpRestExceptionXWikiImpl(
e.getMessage(), e.getCause());
throw e1;
}
this.corefacade.resourceNotification(modelSetId, modelId, artifactId,
resourceId, resourceType, actionType, userId);
}
}