mirror of
https://bitbucket.org/wisemapping/wisemapping-open-source.git
synced 2024-11-26 07:44:56 +01:00
Edition lock is working. Working on user interation.
This commit is contained in:
parent
742fa9d03e
commit
d17b8397f7
@ -18,14 +18,19 @@
|
|||||||
|
|
||||||
mindplot.RESTPersistenceManager = new Class({
|
mindplot.RESTPersistenceManager = new Class({
|
||||||
Extends:mindplot.PersistenceManager,
|
Extends:mindplot.PersistenceManager,
|
||||||
initialize:function (saveUrl, revertUrl, lockUrl) {
|
initialize:function (options) {
|
||||||
this.parent();
|
this.parent();
|
||||||
$assert(saveUrl, "saveUrl can not be null");
|
$assert(options.saveUrl, "saveUrl can not be null");
|
||||||
$assert(revertUrl, "revertUrl can not be null");
|
$assert(options.revertUrl, "revertUrl can not be null");
|
||||||
this.saveUrl = saveUrl;
|
$assert(options.lockUrl, "lockUrl can not be null");
|
||||||
this.revertUrl = revertUrl;
|
$assert(options.session, "session can not be null");
|
||||||
this.lockUrl = lockUrl;
|
$assert(options.timestamp, "timestamp can not be null");
|
||||||
this.timestamp = null;
|
|
||||||
|
this.saveUrl = options.saveUrl;
|
||||||
|
this.revertUrl = options.revertUrl;
|
||||||
|
this.lockUrl = options.lockUrl;
|
||||||
|
this.timestamp = options.timestamp;
|
||||||
|
this.session = options.session;
|
||||||
},
|
},
|
||||||
|
|
||||||
saveMapXml:function (mapId, mapXml, pref, saveHistory, events, sync) {
|
saveMapXml:function (mapId, mapXml, pref, saveHistory, events, sync) {
|
||||||
@ -39,6 +44,7 @@ mindplot.RESTPersistenceManager = new Class({
|
|||||||
var persistence = this;
|
var persistence = this;
|
||||||
var query = "minor=" + !saveHistory;
|
var query = "minor=" + !saveHistory;
|
||||||
query = query + (this.timestamp ? "×tamp=" + this.timestamp : "");
|
query = query + (this.timestamp ? "×tamp=" + this.timestamp : "");
|
||||||
|
query = query + (this.session ? "&session=" + this.session : "");
|
||||||
|
|
||||||
var request = new Request({
|
var request = new Request({
|
||||||
url:this.saveUrl.replace("{id}", mapId) + "?" + query,
|
url:this.saveUrl.replace("{id}", mapId) + "?" + query,
|
||||||
|
83
mindplot/src/main/javascript/widget/ErrorNotifier.js
Normal file
83
mindplot/src/main/javascript/widget/ErrorNotifier.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2011] [wisemapping]
|
||||||
|
*
|
||||||
|
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
||||||
|
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
||||||
|
* "powered by wisemapping" text requirement on every single page;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the license at
|
||||||
|
*
|
||||||
|
* http://www.wisemapping.org/license
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mindplot.widget.ToolbarNotifier = new Class({
|
||||||
|
|
||||||
|
initialize:function () {
|
||||||
|
var container = $('headerNotifier');
|
||||||
|
// In case of print,embedded no message is displayed ....
|
||||||
|
if (container) {
|
||||||
|
this._effect = new Fx.Elements(container, {
|
||||||
|
onComplete:function () {
|
||||||
|
container.setStyle('display', 'none');
|
||||||
|
}.bind(this),
|
||||||
|
link:'cancel',
|
||||||
|
duration:8000,
|
||||||
|
transition:Fx.Transitions.Expo.easeInOut
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
logError:function (userMsg) {
|
||||||
|
this.logMessage(userMsg, mindplot.widget.ToolbarNotifier.MsgKind.ERROR);
|
||||||
|
},
|
||||||
|
|
||||||
|
hide:function () {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
logMessage:function (msg, fade) {
|
||||||
|
$assert(msg, 'msg can not be null');
|
||||||
|
|
||||||
|
var container = $('headerNotifier');
|
||||||
|
|
||||||
|
// In case of print,embedded no message is displayed ....
|
||||||
|
if (container) {
|
||||||
|
container.set('text', msg);
|
||||||
|
container.setStyle('display', 'block');
|
||||||
|
container.position({
|
||||||
|
relativeTo:$('header'),
|
||||||
|
position:'upperCenter',
|
||||||
|
edge:'centerTop'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!$defined(fade) || fade) {
|
||||||
|
this._effect.start({
|
||||||
|
0:{
|
||||||
|
opacity:[1, 0]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
container.setStyle('opacity', '1');
|
||||||
|
this._effect.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
mindplot.widget.ToolbarNotifier.MsgKind = {
|
||||||
|
INFO:1,
|
||||||
|
WARNING:2,
|
||||||
|
ERROR:3,
|
||||||
|
FATAL:4
|
||||||
|
};
|
||||||
|
|
||||||
|
var toolbarNotifier = new mindplot.widget.ToolbarNotifier();
|
||||||
|
$notify = toolbarNotifier.logMessage.bind(toolbarNotifier);
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2011] [wisemapping]
|
||||||
|
*
|
||||||
|
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
||||||
|
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
||||||
|
* "powered by wisemapping" text requirement on every single page;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the license at
|
||||||
|
*
|
||||||
|
* http://www.wisemapping.org/license
|
||||||
|
*
|
||||||
|
* 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 com.wisemapping.exceptions;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class EditionSessionExpiredException
|
||||||
|
extends ClientException
|
||||||
|
{
|
||||||
|
public static final String MSG_KEY = "MINDMAP_TIMESTAMP_OUTDATED";
|
||||||
|
|
||||||
|
public EditionSessionExpiredException(@NotNull String msg)
|
||||||
|
{
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
protected String getMsgBundleKey() {
|
||||||
|
return MSG_KEY;
|
||||||
|
}
|
||||||
|
}
|
@ -20,12 +20,12 @@ package com.wisemapping.exceptions;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class MindmapOutdatedException
|
public class MultipleSessionsOpenException
|
||||||
extends ClientException
|
extends ClientException
|
||||||
{
|
{
|
||||||
public static final String MSG_KEY = "MINDMAP_TIMESTAMP_OUTDATED";
|
public static final String MSG_KEY = "MINDMAP_TIMESTAMP_OUTDATED";
|
||||||
|
|
||||||
public MindmapOutdatedException(@NotNull String msg)
|
public MultipleSessionsOpenException(@NotNull String msg)
|
||||||
{
|
{
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2011] [wisemapping]
|
||||||
|
*
|
||||||
|
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
|
||||||
|
* It is basically the Apache License, Version 2.0 (the "License") plus the
|
||||||
|
* "powered by wisemapping" text requirement on every single page;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the license at
|
||||||
|
*
|
||||||
|
* http://www.wisemapping.org/license
|
||||||
|
*
|
||||||
|
* 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 com.wisemapping.exceptions;
|
||||||
|
|
||||||
|
import com.wisemapping.model.Collaborator;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class SessionExpiredException
|
||||||
|
extends ClientException
|
||||||
|
{
|
||||||
|
public static final String MSG_KEY = "MINDMAP_TIMESTAMP_OUTDATED";
|
||||||
|
|
||||||
|
public SessionExpiredException(@NotNull String msg,@NotNull Collaborator newEditor)
|
||||||
|
{
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
protected String getMsgBundleKey() {
|
||||||
|
return MSG_KEY;
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ import com.wisemapping.model.Mindmap;
|
|||||||
import com.wisemapping.model.MindMapHistory;
|
import com.wisemapping.model.MindMapHistory;
|
||||||
import com.wisemapping.model.User;
|
import com.wisemapping.model.User;
|
||||||
import com.wisemapping.security.Utils;
|
import com.wisemapping.security.Utils;
|
||||||
|
import com.wisemapping.service.LockInfo;
|
||||||
import com.wisemapping.service.LockManager;
|
import com.wisemapping.service.LockManager;
|
||||||
import com.wisemapping.service.MindmapService;
|
import com.wisemapping.service.MindmapService;
|
||||||
import com.wisemapping.view.MindMapBean;
|
import com.wisemapping.view.MindMapBean;
|
||||||
@ -147,7 +148,7 @@ public class MindmapController {
|
|||||||
return showEditorPage(id, model, true);
|
return showEditorPage(id, model, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String showEditorPage(int id, @NotNull final Model model, boolean requiresLock) throws AccessDeniedSecurityException, LockException {
|
private String showEditorPage(int id, @NotNull final Model model, boolean requiresLock) throws WiseMappingException {
|
||||||
final MindMapBean mindmapBean = findMindmapBean(id);
|
final MindMapBean mindmapBean = findMindmapBean(id);
|
||||||
final Mindmap mindmap = mindmapBean.getDelegated();
|
final Mindmap mindmap = mindmapBean.getDelegated();
|
||||||
final User collaborator = Utils.getUser();
|
final User collaborator = Utils.getUser();
|
||||||
@ -159,10 +160,13 @@ public class MindmapController {
|
|||||||
final LockManager lockManager = this.mindmapService.getLockManager();
|
final LockManager lockManager = this.mindmapService.getLockManager();
|
||||||
if (lockManager.isLocked(mindmap) && !lockManager.isLockedBy(mindmap, collaborator)) {
|
if (lockManager.isLocked(mindmap) && !lockManager.isLockedBy(mindmap, collaborator)) {
|
||||||
readOnlyMode = true;
|
readOnlyMode = true;
|
||||||
model.addAttribute("lockedBy", lockManager.getLockInfo(mindmap));
|
|
||||||
} else {
|
} else {
|
||||||
lockManager.lock(mindmap, collaborator);
|
final long session = lockManager.generateSession();
|
||||||
|
final LockInfo lock = lockManager.lock(mindmap, collaborator, session);
|
||||||
|
model.addAttribute("lockTimestamp", lock.getTimestamp());
|
||||||
|
model.addAttribute("lockSession", session);
|
||||||
}
|
}
|
||||||
|
model.addAttribute("lockInfo", lockManager.getLockInfo(mindmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set render attributes ...
|
// Set render attributes ...
|
||||||
@ -176,12 +180,12 @@ public class MindmapController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "maps/{id}/view", method = RequestMethod.GET)
|
@RequestMapping(value = "maps/{id}/view", method = RequestMethod.GET)
|
||||||
public String showMindmapViewerPage(@PathVariable int id, @NotNull Model model) throws LockException, AccessDeniedSecurityException {
|
public String showMindmapViewerPage(@PathVariable int id, @NotNull Model model) throws WiseMappingException {
|
||||||
return showEditorPage(id, model, false);
|
return showEditorPage(id, model, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "maps/{id}/try", method = RequestMethod.GET)
|
@RequestMapping(value = "maps/{id}/try", method = RequestMethod.GET)
|
||||||
public String showMindmapTryPage(@PathVariable int id, @NotNull Model model) throws LockException, AccessDeniedSecurityException {
|
public String showMindmapTryPage(@PathVariable int id, @NotNull Model model) throws WiseMappingException {
|
||||||
final String result = showEditorPage(id, model, false);
|
final String result = showEditorPage(id, model, false);
|
||||||
model.addAttribute("memoryPersistence", true);
|
model.addAttribute("memoryPersistence", true);
|
||||||
return result;
|
return result;
|
||||||
|
@ -19,9 +19,7 @@
|
|||||||
package com.wisemapping.rest;
|
package com.wisemapping.rest;
|
||||||
|
|
||||||
|
|
||||||
import com.wisemapping.exceptions.ImportUnexpectedException;
|
import com.wisemapping.exceptions.*;
|
||||||
import com.wisemapping.exceptions.MindmapOutdatedException;
|
|
||||||
import com.wisemapping.exceptions.WiseMappingException;
|
|
||||||
import com.wisemapping.importer.ImportFormat;
|
import com.wisemapping.importer.ImportFormat;
|
||||||
import com.wisemapping.importer.Importer;
|
import com.wisemapping.importer.Importer;
|
||||||
import com.wisemapping.importer.ImporterException;
|
import com.wisemapping.importer.ImporterException;
|
||||||
@ -30,10 +28,10 @@ import com.wisemapping.model.*;
|
|||||||
import com.wisemapping.rest.model.*;
|
import com.wisemapping.rest.model.*;
|
||||||
import com.wisemapping.security.Utils;
|
import com.wisemapping.security.Utils;
|
||||||
import com.wisemapping.service.CollaborationException;
|
import com.wisemapping.service.CollaborationException;
|
||||||
|
import com.wisemapping.service.LockInfo;
|
||||||
import com.wisemapping.service.LockManager;
|
import com.wisemapping.service.LockManager;
|
||||||
import com.wisemapping.service.MindmapService;
|
import com.wisemapping.service.MindmapService;
|
||||||
import com.wisemapping.validator.MapInfoValidator;
|
import com.wisemapping.validator.MapInfoValidator;
|
||||||
import org.apache.log4j.Logger;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
@ -141,7 +139,7 @@ public class MindmapController extends BaseController {
|
|||||||
|
|
||||||
@RequestMapping(method = RequestMethod.PUT, value = "/maps/{id}/document", consumes = {"application/xml", "application/json"}, produces = {"application/json", "text/html", "application/xml"})
|
@RequestMapping(method = RequestMethod.PUT, value = "/maps/{id}/document", consumes = {"application/xml", "application/json"}, produces = {"application/json", "text/html", "application/xml"})
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public long updateDocument(@RequestBody RestMindmap restMindmap, @PathVariable int id, @RequestParam(required = false) boolean minor, @RequestParam(required = false) Long timestamp) throws WiseMappingException, IOException {
|
public long updateDocument(@RequestBody RestMindmap restMindmap, @PathVariable int id, @RequestParam(required = false) boolean minor, @RequestParam(required = false) Long timestamp, @RequestParam(required = false) Long session) throws WiseMappingException, IOException {
|
||||||
|
|
||||||
final Mindmap mindmap = mindmapService.findMindmapById(id);
|
final Mindmap mindmap = mindmapService.findMindmapById(id);
|
||||||
final User user = Utils.getUser();
|
final User user = Utils.getUser();
|
||||||
@ -152,10 +150,8 @@ public class MindmapController extends BaseController {
|
|||||||
throw new IllegalArgumentException("Map properties can not be null");
|
throw new IllegalArgumentException("Map properties can not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that there we are not overwriting an already existing map ...
|
// Could the map be updated ?
|
||||||
if (timestamp != null && mindmap.getLastModificationTime().getTimeInMillis() > timestamp) {
|
checkUpdate(mindmap, user, session, timestamp);
|
||||||
throw new MindmapOutdatedException("Mindmap timestamp out of sync. Client timestamp: " + timestamp + ", DB Timestamp:" + timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update collaboration properties ...
|
// Update collaboration properties ...
|
||||||
final CollaborationProperties collaborationProperties = mindmap.findCollaborationProperties(user);
|
final CollaborationProperties collaborationProperties = mindmap.findCollaborationProperties(user);
|
||||||
@ -172,8 +168,36 @@ public class MindmapController extends BaseController {
|
|||||||
logger.debug("Mindmap save completed:" + restMindmap.getXml());
|
logger.debug("Mindmap save completed:" + restMindmap.getXml());
|
||||||
saveMindmap(minor, mindmap, user);
|
saveMindmap(minor, mindmap, user);
|
||||||
|
|
||||||
// Return last update timestamp ...
|
// Update edition timeout ...
|
||||||
return mindmap.getLastModificationTime().getTimeInMillis();
|
final LockManager lockManager = mindmapService.getLockManager();
|
||||||
|
final LockInfo lockInfo = lockManager.updateExpirationTimeout(mindmap, user, session);
|
||||||
|
return lockInfo.getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkUpdate(@NotNull Mindmap mindmap, @NotNull User user, long session, long timestamp) throws WiseMappingException {
|
||||||
|
// The lock was lost, reclaim as the ownership of it.
|
||||||
|
final LockManager lockManager = mindmapService.getLockManager();
|
||||||
|
final boolean lockLost = lockManager.isLocked(mindmap);
|
||||||
|
if (!lockLost) {
|
||||||
|
lockManager.lock(mindmap, user, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
final LockInfo lockInfo = lockManager.getLockInfo(mindmap);
|
||||||
|
if (lockInfo.getCollaborator().equals(user)) {
|
||||||
|
final boolean outdated = mindmap.getLastModificationTime().getTimeInMillis() > timestamp;
|
||||||
|
if (lockInfo.getSession() == session) {
|
||||||
|
// Timestamp might not be returned to the client. This try to cover this case, ignoring the client timestamp check.
|
||||||
|
final User lastEditor = mindmap.getLastEditor();
|
||||||
|
if (outdated && (lockInfo.getPreviousTimestamp() != timestamp || lastEditor == null || !lastEditor.equals(user))) {
|
||||||
|
throw new MultipleSessionsOpenException("The map has been updated and not by you. Session lost.");
|
||||||
|
}
|
||||||
|
} else if (outdated) {
|
||||||
|
throw new MultipleSessionsOpenException("The map has been updated and not by you. Session lost.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new SessionExpiredException("You have lost the edition session", lockInfo.getCollaborator());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -361,7 +385,13 @@ public class MindmapController extends BaseController {
|
|||||||
final User user = Utils.getUser();
|
final User user = Utils.getUser();
|
||||||
final LockManager lockManager = mindmapService.getLockManager();
|
final LockManager lockManager = mindmapService.getLockManager();
|
||||||
final Mindmap mindmap = mindmapService.findMindmapById(id);
|
final Mindmap mindmap = mindmapService.findMindmapById(id);
|
||||||
lockManager.updateLock(Boolean.parseBoolean(value), mindmap, user);
|
|
||||||
|
final boolean lock = Boolean.parseBoolean(value);
|
||||||
|
if (!lock) {
|
||||||
|
lockManager.unlock(mindmap, user);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("REST lock must be implemented.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.DELETE, value = "/maps/batch")
|
@RequestMapping(method = RequestMethod.DELETE, value = "/maps/batch")
|
||||||
|
@ -24,14 +24,23 @@ import java.util.Set;
|
|||||||
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
|
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
|
||||||
isGetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY)
|
isGetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY)
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class RestMindmapLock {
|
public class RestLockInfo {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private Collaborator user;
|
final private Collaborator user;
|
||||||
@Nullable
|
|
||||||
private LockInfo lockInfo;
|
|
||||||
|
|
||||||
public RestMindmapLock(@Nullable LockInfo lockInfo, @NotNull Collaborator collaborator) {
|
@Nullable
|
||||||
|
final private LockInfo lockInfo;
|
||||||
|
|
||||||
|
// This is required only for compliance with the JAXB serializer.
|
||||||
|
public RestLockInfo(){
|
||||||
|
|
||||||
|
this.lockInfo = null;
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
this.user = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestLockInfo(@Nullable LockInfo lockInfo, @NotNull Collaborator collaborator) {
|
||||||
|
|
||||||
this.lockInfo = lockInfo;
|
this.lockInfo = lockInfo;
|
||||||
this.user = collaborator;
|
this.user = collaborator;
|
||||||
@ -52,4 +61,13 @@ public class RestMindmapLock {
|
|||||||
public void setLockedByMe(boolean lockedForMe) {
|
public void setLockedByMe(boolean lockedForMe) {
|
||||||
// Ignore ...
|
// Ignore ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return lockInfo != null ? lockInfo.getTimestamp() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(long value) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -19,6 +19,7 @@
|
|||||||
package com.wisemapping.service;
|
package com.wisemapping.service;
|
||||||
|
|
||||||
import com.wisemapping.model.Collaborator;
|
import com.wisemapping.model.Collaborator;
|
||||||
|
import com.wisemapping.model.Mindmap;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
@ -26,11 +27,15 @@ import java.util.Calendar;
|
|||||||
public class LockInfo {
|
public class LockInfo {
|
||||||
final private Collaborator collaborator;
|
final private Collaborator collaborator;
|
||||||
private Calendar timeout;
|
private Calendar timeout;
|
||||||
private static int EXPIRATION_MIN = 25;
|
private long session;
|
||||||
|
private static int EXPIRATION_MIN = 30;
|
||||||
|
private long timestamp = -1;
|
||||||
|
private long previousTimestamp;
|
||||||
|
|
||||||
public LockInfo(@NotNull Collaborator collaborator) {
|
public LockInfo(@NotNull Collaborator collaborator, @NotNull Mindmap mindmap, long session) {
|
||||||
this.collaborator = collaborator;
|
this.collaborator = collaborator;
|
||||||
this.updateTimeout();
|
this.updateTimeout();
|
||||||
|
this.updateTimestamp(mindmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collaborator getCollaborator() {
|
public Collaborator getCollaborator() {
|
||||||
@ -38,7 +43,7 @@ public class LockInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return timeout.before(Calendar.getInstance());
|
return timeout.before(Calendar.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateTimeout() {
|
public void updateTimeout() {
|
||||||
@ -47,4 +52,25 @@ public class LockInfo {
|
|||||||
this.timeout = calendar;
|
this.timeout = calendar;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getSession() {
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSession(long session) {
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPreviousTimestamp() {
|
||||||
|
return previousTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateTimestamp(@NotNull Mindmap mindmap) {
|
||||||
|
this.previousTimestamp = this.timestamp;
|
||||||
|
this.timestamp = mindmap.getLastModificationTime().getTimeInMillis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package com.wisemapping.service;
|
package com.wisemapping.service;
|
||||||
|
|
||||||
import com.wisemapping.exceptions.AccessDeniedSecurityException;
|
import com.wisemapping.exceptions.AccessDeniedSecurityException;
|
||||||
|
import com.wisemapping.exceptions.ClientException;
|
||||||
import com.wisemapping.exceptions.LockException;
|
import com.wisemapping.exceptions.LockException;
|
||||||
import com.wisemapping.exceptions.WiseMappingException;
|
import com.wisemapping.exceptions.WiseMappingException;
|
||||||
import com.wisemapping.model.Collaborator;
|
import com.wisemapping.model.Collaborator;
|
||||||
@ -31,13 +32,13 @@ public interface LockManager {
|
|||||||
|
|
||||||
LockInfo getLockInfo(@NotNull Mindmap mindmap);
|
LockInfo getLockInfo(@NotNull Mindmap mindmap);
|
||||||
|
|
||||||
void updateExpirationTimeout(@NotNull Mindmap mindmap, @NotNull Collaborator user);
|
LockInfo updateExpirationTimeout(@NotNull Mindmap mindmap, @NotNull Collaborator user,long session);
|
||||||
|
|
||||||
void unlock(@NotNull Mindmap mindmap, @NotNull Collaborator user) throws LockException, AccessDeniedSecurityException;
|
void unlock(@NotNull Mindmap mindmap, @NotNull Collaborator user) throws LockException, AccessDeniedSecurityException;
|
||||||
|
|
||||||
boolean isLockedBy(@NotNull Mindmap mindmap, @NotNull Collaborator collaborator);
|
boolean isLockedBy(@NotNull Mindmap mindmap, @NotNull Collaborator collaborator);
|
||||||
|
|
||||||
void lock(@NotNull Mindmap mindmap, @NotNull Collaborator user) throws AccessDeniedSecurityException, LockException;
|
LockInfo lock(@NotNull Mindmap mindmap, @NotNull Collaborator user, long session) throws WiseMappingException;
|
||||||
|
|
||||||
void updateLock(boolean value, Mindmap mindmap, User user) throws WiseMappingException;
|
long generateSession();
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import com.wisemapping.exceptions.WiseMappingException;
|
|||||||
import com.wisemapping.model.CollaborationRole;
|
import com.wisemapping.model.CollaborationRole;
|
||||||
import com.wisemapping.model.Collaborator;
|
import com.wisemapping.model.Collaborator;
|
||||||
import com.wisemapping.model.Mindmap;
|
import com.wisemapping.model.Mindmap;
|
||||||
import com.wisemapping.model.User;
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -36,6 +35,15 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* En caso que no sea posible grabar por que se perdio el lock, usar mensaje de error para explicar el por que...
|
* En caso que no sea posible grabar por que se perdio el lock, usar mensaje de error para explicar el por que...
|
||||||
* Mensaje modal explicando que el mapa esta siendo editado, por eso no es posible edilarlo....
|
* Mensaje modal explicando que el mapa esta siendo editado, por eso no es posible edilarlo....
|
||||||
* Internacionalizacion de los mensaje ...
|
* Internacionalizacion de los mensaje ...
|
||||||
|
* Logout limpiar las sessiones ...
|
||||||
|
*
|
||||||
|
* Casos:
|
||||||
|
* - Usuario pierde el lock:
|
||||||
|
* - Y grabo con la misma sessions y el timestap ok.
|
||||||
|
* - Y grabo con la misma session y el timestap esta mal
|
||||||
|
* - Y grabo con distinta sessions
|
||||||
|
* -
|
||||||
|
* - Usuario pierde el lock, pero intenta grabar camio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class LockManagerImpl implements LockManager {
|
class LockManagerImpl implements LockManager {
|
||||||
@ -44,30 +52,6 @@ class LockManagerImpl implements LockManager {
|
|||||||
final static Timer expirationTimer = new Timer();
|
final static Timer expirationTimer = new Timer();
|
||||||
final private static Logger logger = Logger.getLogger("com.wisemapping.service.LockManager");
|
final private static Logger logger = Logger.getLogger("com.wisemapping.service.LockManager");
|
||||||
|
|
||||||
public LockManagerImpl() {
|
|
||||||
lockInfoByMapId = new ConcurrentHashMap<Integer, LockInfo>();
|
|
||||||
expirationTimer.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
logger.debug("Lock expiration scheduler started. Current locks:" + lockInfoByMapId.keySet());
|
|
||||||
|
|
||||||
final List<Integer> toRemove = new ArrayList<Integer>();
|
|
||||||
final Set<Integer> mapIds = lockInfoByMapId.keySet();
|
|
||||||
for (Integer mapId : mapIds) {
|
|
||||||
final LockInfo lockInfo = lockInfoByMapId.get(mapId);
|
|
||||||
if (lockInfo.isExpired()) {
|
|
||||||
toRemove.add(mapId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Integer mapId : toRemove) {
|
|
||||||
unlock(mapId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, ONE_MINUTE_MILLISECONDS, ONE_MINUTE_MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLocked(@NotNull Mindmap mindmap) {
|
public boolean isLocked(@NotNull Mindmap mindmap) {
|
||||||
return this.getLockInfo(mindmap) != null;
|
return this.getLockInfo(mindmap) != null;
|
||||||
@ -79,18 +63,21 @@ class LockManagerImpl implements LockManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateExpirationTimeout(@NotNull Mindmap mindmap, @NotNull Collaborator user) {
|
public LockInfo updateExpirationTimeout(@NotNull Mindmap mindmap, @NotNull Collaborator user, long session) {
|
||||||
if (this.isLocked(mindmap)) {
|
if (!this.isLocked(mindmap)) {
|
||||||
final LockInfo lockInfo = this.getLockInfo(mindmap);
|
|
||||||
if (!lockInfo.getCollaborator().equals(user)) {
|
|
||||||
throw new IllegalStateException("Could not update map lock timeout if you are not the locking user. User:" + lockInfo.getCollaborator() + ", " + user);
|
|
||||||
}
|
|
||||||
lockInfo.updateTimeout();
|
|
||||||
logger.debug("Timeout updated for:" + mindmap.getId());
|
|
||||||
|
|
||||||
}else {
|
|
||||||
throw new IllegalStateException("Lock lost for map. No update possible.");
|
throw new IllegalStateException("Lock lost for map. No update possible.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final LockInfo result = this.getLockInfo(mindmap);
|
||||||
|
if (!result.getCollaborator().equals(user)) {
|
||||||
|
throw new IllegalStateException("Could not update map lock timeout if you are not the locking user. User:" + result.getCollaborator() + ", " + user);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.updateTimeout();
|
||||||
|
result.setSession(session);
|
||||||
|
result.updateTimestamp(mindmap);
|
||||||
|
logger.debug("Timeout updated for:" + mindmap.getId());
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -122,7 +109,8 @@ class LockManagerImpl implements LockManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void lock(@NotNull Mindmap mindmap, @NotNull Collaborator user) throws AccessDeniedSecurityException, LockException {
|
@NotNull
|
||||||
|
public LockInfo lock(@NotNull Mindmap mindmap, @NotNull Collaborator user, long session) throws WiseMappingException {
|
||||||
if (isLocked(mindmap) && !isLockedBy(mindmap, user)) {
|
if (isLocked(mindmap) && !isLockedBy(mindmap, user)) {
|
||||||
throw new LockException("Invalid lock, this should not happen");
|
throw new LockException("Invalid lock, this should not happen");
|
||||||
}
|
}
|
||||||
@ -131,24 +119,46 @@ class LockManagerImpl implements LockManager {
|
|||||||
throw new AccessDeniedSecurityException("Invalid lock, this should not happen");
|
throw new AccessDeniedSecurityException("Invalid lock, this should not happen");
|
||||||
}
|
}
|
||||||
|
|
||||||
final LockInfo lockInfo = lockInfoByMapId.get(mindmap.getId());
|
LockInfo result = lockInfoByMapId.get(mindmap.getId());
|
||||||
if (lockInfo != null) {
|
if (result != null) {
|
||||||
// Update timeout only...
|
// Update timeout only...
|
||||||
logger.debug("Update timestamp:" + mindmap.getId());
|
logger.debug("Update timestamp:" + mindmap.getId());
|
||||||
updateExpirationTimeout(mindmap, user);
|
updateExpirationTimeout(mindmap, user, session);
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Lock map id:" + mindmap.getId());
|
logger.debug("Lock map id:" + mindmap.getId());
|
||||||
lockInfoByMapId.put(mindmap.getId(), new LockInfo(user));
|
result = new LockInfo(user, mindmap, session);
|
||||||
|
lockInfoByMapId.put(mindmap.getId(), result);
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateLock(boolean lock, @NotNull Mindmap mindmap, @NotNull User user) throws WiseMappingException {
|
public long generateSession() {
|
||||||
if (lock) {
|
return System.nanoTime();
|
||||||
this.lock(mindmap, user);
|
|
||||||
} else {
|
|
||||||
this.unlock(mindmap, user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LockManagerImpl() {
|
||||||
|
lockInfoByMapId = new ConcurrentHashMap<Integer, LockInfo>();
|
||||||
|
expirationTimer.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
logger.debug("Lock expiration scheduler started. Current locks:" + lockInfoByMapId.keySet());
|
||||||
|
|
||||||
|
final List<Integer> toRemove = new ArrayList<Integer>();
|
||||||
|
final Set<Integer> mapIds = lockInfoByMapId.keySet();
|
||||||
|
for (Integer mapId : mapIds) {
|
||||||
|
final LockInfo lockInfo = lockInfoByMapId.get(mapId);
|
||||||
|
if (lockInfo.isExpired()) {
|
||||||
|
toRemove.add(mapId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Integer mapId : toRemove) {
|
||||||
|
unlock(mapId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, ONE_MINUTE_MILLISECONDS, ONE_MINUTE_MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -100,10 +100,6 @@ public class MindmapServiceImpl
|
|||||||
throw new WiseMappingException("The tile can not be empty");
|
throw new WiseMappingException("The tile can not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update edition timeout ...
|
|
||||||
final LockManager lockManager = this.getLockManager();
|
|
||||||
lockManager.updateExpirationTimeout(mindMap, Utils.getUser());
|
|
||||||
|
|
||||||
mindmapManager.updateMindmap(mindMap, saveHistory);
|
mindmapManager.updateMindmap(mindMap, saveHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
<value>com.wisemapping.rest.model.RestCollaboration</value>
|
<value>com.wisemapping.rest.model.RestCollaboration</value>
|
||||||
<value>com.wisemapping.rest.model.RestCollaborationList</value>
|
<value>com.wisemapping.rest.model.RestCollaborationList</value>
|
||||||
<value>com.wisemapping.rest.model.RestLogItem</value>
|
<value>com.wisemapping.rest.model.RestLogItem</value>
|
||||||
|
<value>com.wisemapping.rest.model.RestLockInfo</value>
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
@ -34,8 +34,16 @@
|
|||||||
|
|
||||||
// Configure designer options ...
|
// Configure designer options ...
|
||||||
var options = loadDesignerOptions();
|
var options = loadDesignerOptions();
|
||||||
<c:if test="${!memoryPersistence}">
|
<c:if test="${!memoryPersistence && !readOnlyMode}">
|
||||||
options.persistenceManager = new mindplot.RESTPersistenceManager("service/maps/{id}/document", "service/maps/{id}/history/latest","service/maps/{id}/lock");
|
options.persistenceManager = new mindplot.RESTPersistenceManager(
|
||||||
|
{
|
||||||
|
saveUrl:"service/maps/{id}/document",
|
||||||
|
revertUrl:"service/maps/{id}/history/latest",
|
||||||
|
lockUrl:"service/maps/{id}/lock",
|
||||||
|
timestamp: ${lockTimestamp},
|
||||||
|
session: ${lockSession}
|
||||||
|
}
|
||||||
|
);
|
||||||
</c:if>
|
</c:if>
|
||||||
var userOptions = ${mindmap.properties};
|
var userOptions = ${mindmap.properties};
|
||||||
options.zoom = userOptions.zoom;
|
options.zoom = userOptions.zoom;
|
||||||
|
Loading…
Reference in New Issue
Block a user