- Finish exclusive locking support.

This commit is contained in:
Paulo Gustavo Veiga
2012-09-30 17:15:01 -03:00
committed by Paulo Gustavo Veiga
parent e5e2e86fce
commit a179875fee
24 changed files with 549 additions and 104 deletions

View File

@@ -0,0 +1,35 @@
/*
* 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 LockException
extends ClientException
{
public LockException(@NotNull String message) {
super(message);
}
@NotNull
@Override
protected String getMsgBundleKey() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}

View File

@@ -1,29 +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;
public class UnexpectedArgumentException
extends Exception
{
public UnexpectedArgumentException(String msg)
{
super(msg);
}
}
/*
* 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 MindmapOutdatedException
extends ClientException
{
public static final String MSG_KEY = "MINDMAP_TIMESTAMP_OUTDATED";
public MindmapOutdatedException(@NotNull String msg)
{
super(msg);
}
@NotNull
@Override
protected String getMsgBundleKey() {
return MSG_KEY;
}
}

View File

@@ -138,10 +138,6 @@ public class Mindmap {
return lastModificationTime;
}
public Date getLastModificationDate() {
return new Date();
}
public void setLastModificationTime(Calendar lastModificationTime) {
this.lastModificationTime = lastModificationTime;
}

View File

@@ -20,6 +20,8 @@ package com.wisemapping.model;
import org.jetbrains.annotations.Nullable;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import java.io.Serializable;
import java.util.*;

View File

@@ -19,12 +19,15 @@
package com.wisemapping.ncontroller;
import com.wisemapping.exceptions.AccessDeniedSecurityException;
import com.wisemapping.exceptions.LockException;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.CollaborationRole;
import com.wisemapping.model.Mindmap;
import com.wisemapping.model.MindMapHistory;
import com.wisemapping.model.User;
import com.wisemapping.security.Utils;
import com.wisemapping.service.LockManager;
import com.wisemapping.service.MindmapService;
import com.wisemapping.view.MindMapBean;
import org.jetbrains.annotations.NotNull;
@@ -140,33 +143,47 @@ public class MindmapController {
}
@RequestMapping(value = "maps/{id}/edit", method = RequestMethod.GET)
public String showMindmapEditorPage(@PathVariable int id, @NotNull Model model) {
public String showMindmapEditorPage(@PathVariable int id, @NotNull Model model) throws WiseMappingException {
return showEditorPage(id, model, true);
}
private String showEditorPage(int id, @NotNull final Model model, boolean requiresLock) throws AccessDeniedSecurityException, LockException {
final MindMapBean mindmapBean = findMindmapBean(id);
final Mindmap mindmap = mindmapBean.getDelegated();
final User collaborator = Utils.getUser();
final Locale locale = LocaleContextHolder.getLocale();
// Is the mindmap locked ?.
boolean readOnlyMode = !requiresLock || !mindmap.hasPermissions(collaborator, CollaborationRole.EDITOR);
if (!readOnlyMode) {
final LockManager lockManager = this.mindmapService.getLockManager();
if (lockManager.isLocked(mindmap) && !lockManager.isLockedBy(mindmap, collaborator)) {
readOnlyMode = true;
model.addAttribute("lockedBy", lockManager.getLockInfo(mindmap));
} else {
lockManager.lock(mindmap, collaborator);
}
}
// Set render attributes ...
model.addAttribute("mindmap", mindmapBean);
// Configure default locale for the editor ...
final Locale locale = LocaleContextHolder.getLocale();
model.addAttribute("locale", locale.toString().toLowerCase());
final User collaborator = Utils.getUser();
model.addAttribute("principal", collaborator);
model.addAttribute("readOnlyMode", !mindmap.hasPermissions(collaborator, CollaborationRole.EDITOR));
model.addAttribute("readOnlyMode", readOnlyMode);
return "mindmapEditor";
}
@RequestMapping(value = "maps/{id}/view", method = RequestMethod.GET)
public String showMindmapViewerPage(@PathVariable int id, @NotNull Model model) {
final String result = showMindmapEditorPage(id, model);
model.addAttribute("readOnlyMode", true);
return result;
public String showMindmapViewerPage(@PathVariable int id, @NotNull Model model) throws LockException, AccessDeniedSecurityException {
return showEditorPage(id, model, false);
}
@RequestMapping(value = "maps/{id}/try", method = RequestMethod.GET)
public String showMindmapTryPage(@PathVariable int id, @NotNull Model model) {
final String result = showMindmapEditorPage(id, model);
public String showMindmapTryPage(@PathVariable int id, @NotNull Model model) throws LockException, AccessDeniedSecurityException {
final String result = showEditorPage(id, model, false);
model.addAttribute("memoryPersistence", true);
model.addAttribute("readOnlyMode", false);
return result;
}
@@ -213,11 +230,7 @@ public class MindmapController {
}
private Mindmap findMindmap(long mapId) {
final Mindmap mindmap = mindmapService.findMindmapById((int) mapId);
if (mindmap == null) {
throw new IllegalArgumentException("Mindmap could not be found");
}
return mindmap;
return mindmapService.findMindmapById((int) mapId);
}
private MindMapBean findMindmapBean(long mapId) {

View File

@@ -24,6 +24,7 @@ import com.wisemapping.mail.NotificationService;
import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestErrors;
import com.wisemapping.security.Utils;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -41,6 +42,8 @@ import java.util.Locale;
public class BaseController {
final protected static Logger logger = Logger.getLogger("com.wisemapping.rest");
@Qualifier("messageSource")
@Autowired
private ResourceBundleMessageSource messageSource;

View File

@@ -20,6 +20,7 @@ package com.wisemapping.rest;
import com.wisemapping.exceptions.ImportUnexpectedException;
import com.wisemapping.exceptions.MindmapOutdatedException;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.importer.ImportFormat;
import com.wisemapping.importer.Importer;
@@ -29,8 +30,10 @@ import com.wisemapping.model.*;
import com.wisemapping.rest.model.*;
import com.wisemapping.security.Utils;
import com.wisemapping.service.CollaborationException;
import com.wisemapping.service.LockManager;
import com.wisemapping.service.MindmapService;
import com.wisemapping.validator.MapInfoValidator;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -49,6 +52,7 @@ import java.util.*;
@Controller
public class MindmapController extends BaseController {
public static final String LATEST_HISTORY_REVISION = "latest";
@Qualifier("mindmapService")
@Autowired
@@ -136,8 +140,8 @@ 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"})
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateDocument(@RequestBody RestMindmap restMindmap, @PathVariable int id, @RequestParam(required = false) boolean minor) throws WiseMappingException, IOException {
@ResponseBody
public long updateDocument(@RequestBody RestMindmap restMindmap, @PathVariable int id, @RequestParam(required = false) boolean minor, @RequestParam(required = false) Long timestamp) throws WiseMappingException, IOException {
final Mindmap mindmap = mindmapService.findMindmapById(id);
final User user = Utils.getUser();
@@ -148,6 +152,11 @@ public class MindmapController extends BaseController {
throw new IllegalArgumentException("Map properties can not be null");
}
// Check that there we are not overwriting an already existing map ...
if (timestamp != null && mindmap.getLastModificationTime().getTimeInMillis() > timestamp) {
throw new MindmapOutdatedException("Mindmap timestamp out of sync. Client timestamp: " + timestamp + ", DB Timestamp:" + timestamp);
}
// Update collaboration properties ...
final CollaborationProperties collaborationProperties = mindmap.findCollaborationProperties(user);
collaborationProperties.setMindmapProperties(properties);
@@ -160,7 +169,11 @@ public class MindmapController extends BaseController {
mindmap.setXmlStr(xml);
// Update map ...
logger.debug("Mindmap save completed:" + restMindmap.getXml());
saveMindmap(minor, mindmap, user);
// Return last update timestamp ...
return mindmap.getLastModificationTime().getTimeInMillis();
}
/**
@@ -317,6 +330,14 @@ public class MindmapController extends BaseController {
}
@RequestMapping(method = RequestMethod.DELETE, value = "/maps/{id}")
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateMap(@PathVariable int id) throws IOException, WiseMappingException {
final User user = Utils.getUser();
final Mindmap mindmap = mindmapService.findMindmapById(id);
mindmapService.removeMindmap(mindmap, user);
}
@RequestMapping(method = RequestMethod.PUT, value = "/maps/{id}/starred", consumes = {"text/plain"}, produces = {"application/json", "text/html", "application/xml"})
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateStarredState(@RequestBody String value, @PathVariable int id) throws WiseMappingException {
@@ -334,12 +355,13 @@ public class MindmapController extends BaseController {
mindmapService.updateCollaboration(user, collaboration);
}
@RequestMapping(method = RequestMethod.DELETE, value = "/maps/{id}")
@RequestMapping(method = RequestMethod.PUT, value = "/maps/{id}/lock", consumes = {"text/plain"}, produces = {"application/json", "text/html", "application/xml"})
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void updateMap(@PathVariable int id) throws IOException, WiseMappingException {
public void updateMapLock(@RequestBody String value, @PathVariable int id) throws IOException, WiseMappingException {
final User user = Utils.getUser();
final LockManager lockManager = mindmapService.getLockManager();
final Mindmap mindmap = mindmapService.findMindmapById(id);
mindmapService.removeMindmap(mindmap, user);
lockManager.updateLock(Boolean.parseBoolean(value), mindmap, user);
}
@RequestMapping(method = RequestMethod.DELETE, value = "/maps/batch")

View File

@@ -0,0 +1,55 @@
package com.wisemapping.rest.model;
import com.wisemapping.model.Collaborator;
import com.wisemapping.model.User;
import com.wisemapping.service.LockInfo;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
@XmlRootElement(name = "lock")
@XmlAccessorType(XmlAccessType.PROPERTY)
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.NONE,
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
isGetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class RestMindmapLock {
@NotNull
private Collaborator user;
@Nullable
private LockInfo lockInfo;
public RestMindmapLock(@Nullable LockInfo lockInfo, @NotNull Collaborator collaborator) {
this.lockInfo = lockInfo;
this.user = collaborator;
}
public boolean isLocked() {
return lockInfo != null;
}
public void setLocked(boolean locked) {
// Ignore ...
}
public boolean isLockedByMe() {
return isLocked() && lockInfo != null && lockInfo.getCollaborator().equals(user);
}
public void setLockedByMe(boolean lockedForMe) {
// Ignore ...
}
}

View File

@@ -1,3 +1,21 @@
/*
* 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.security;

View File

@@ -22,7 +22,6 @@ import com.wisemapping.model.Collaborator;
import com.wisemapping.model.Mindmap;
import com.wisemapping.model.User;
import com.wisemapping.exceptions.AccessDeniedSecurityException;
import com.wisemapping.exceptions.UnexpectedArgumentException;
import com.wisemapping.security.Utils;
import com.wisemapping.service.MindmapService;
import org.aopalliance.intercept.MethodInvocation;
@@ -31,7 +30,7 @@ import org.jetbrains.annotations.Nullable;
public abstract class BaseSecurityAdvice {
private MindmapService mindmapService = null;
public void checkRole(MethodInvocation methodInvocation) throws UnexpectedArgumentException, AccessDeniedSecurityException {
public void checkRole(MethodInvocation methodInvocation) throws AccessDeniedSecurityException {
final User user = Utils.getUser();
final Object argument = methodInvocation.getArguments()[0];
boolean isAllowed;
@@ -44,7 +43,7 @@ public abstract class BaseSecurityAdvice {
// Read operation find on the user are allowed ...
isAllowed = user.equals(argument);
} else {
throw new UnexpectedArgumentException("Argument " + argument);
throw new IllegalArgumentException("Argument " + argument);
}
if (!isAllowed) {

View File

@@ -0,0 +1,50 @@
/*
* 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.service;
import com.wisemapping.model.Collaborator;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
public class LockInfo {
final private Collaborator collaborator;
private Calendar timeout;
private static int EXPIRATION_MIN = 25;
public LockInfo(@NotNull Collaborator collaborator) {
this.collaborator = collaborator;
this.updateTimeout();
}
public Collaborator getCollaborator() {
return collaborator;
}
public boolean isExpired() {
return timeout.before(Calendar.getInstance());
}
public void updateTimeout() {
final Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, EXPIRATION_MIN);
this.timeout = calendar;
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.service;
import com.wisemapping.exceptions.AccessDeniedSecurityException;
import com.wisemapping.exceptions.LockException;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.Collaborator;
import com.wisemapping.model.Mindmap;
import com.wisemapping.model.User;
import org.jetbrains.annotations.NotNull;
public interface LockManager {
boolean isLocked(@NotNull Mindmap mindmap);
LockInfo getLockInfo(@NotNull Mindmap mindmap);
void updateExpirationTimeout(@NotNull Mindmap mindmap, @NotNull Collaborator user);
void unlock(@NotNull Mindmap mindmap, @NotNull Collaborator user) throws LockException, AccessDeniedSecurityException;
boolean isLockedBy(@NotNull Mindmap mindmap, @NotNull Collaborator collaborator);
void lock(@NotNull Mindmap mindmap, @NotNull Collaborator user) throws AccessDeniedSecurityException, LockException;
void updateLock(boolean value, Mindmap mindmap, User user) throws WiseMappingException;
}

View File

@@ -0,0 +1,153 @@
/*
* 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.service;
import com.wisemapping.exceptions.AccessDeniedSecurityException;
import com.wisemapping.exceptions.LockException;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.CollaborationRole;
import com.wisemapping.model.Collaborator;
import com.wisemapping.model.Mindmap;
import com.wisemapping.model.User;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/*
* Refresh page should not lost the lock.
* 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....
*/
class LockManagerImpl implements LockManager {
public static final int ONE_MINUTE_MILLISECONDS = 1000 * 60;
final Map<Integer, LockInfo> lockInfoByMapId;
final static Timer expirationTimer = new Timer();
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
public boolean isLocked(@NotNull Mindmap mindmap) {
return this.getLockInfo(mindmap) != null;
}
@Override
public LockInfo getLockInfo(@NotNull Mindmap mindmap) {
return lockInfoByMapId.get(mindmap.getId());
}
@Override
public void updateExpirationTimeout(@NotNull Mindmap mindmap, @NotNull Collaborator user) {
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.");
}
}
@Override
public void unlock(@NotNull Mindmap mindmap, @NotNull Collaborator user) throws LockException, AccessDeniedSecurityException {
if (isLocked(mindmap) && !isLockedBy(mindmap, user)) {
throw new LockException("Lock can be only revoked by the locker.");
}
if (!mindmap.hasPermissions(user, CollaborationRole.EDITOR)) {
throw new AccessDeniedSecurityException("Invalid lock, this should not happen");
}
this.unlock(mindmap.getId());
}
private void unlock(int mapId) {
logger.debug("Unlock map id:" + mapId);
lockInfoByMapId.remove(mapId);
}
@Override
public boolean isLockedBy(@NotNull Mindmap mindmap, @NotNull Collaborator collaborator) {
boolean result = false;
final LockInfo lockInfo = this.getLockInfo(mindmap);
if (lockInfo != null && lockInfo.getCollaborator().equals(collaborator)) {
result = true;
}
return result;
}
@Override
public void lock(@NotNull Mindmap mindmap, @NotNull Collaborator user) throws AccessDeniedSecurityException, LockException {
if (isLocked(mindmap) && !isLockedBy(mindmap, user)) {
throw new LockException("Invalid lock, this should not happen");
}
if (!mindmap.hasPermissions(user, CollaborationRole.EDITOR)) {
throw new AccessDeniedSecurityException("Invalid lock, this should not happen");
}
final LockInfo lockInfo = lockInfoByMapId.get(mindmap.getId());
if (lockInfo != null) {
// Update timeout only...
logger.debug("Update timestamp:" + mindmap.getId());
updateExpirationTimeout(mindmap, user);
} else {
logger.debug("Lock map id:" + mindmap.getId());
lockInfoByMapId.put(mindmap.getId(), new LockInfo(user));
}
}
@Override
public void updateLock(boolean lock, @NotNull Mindmap mindmap, @NotNull User user) throws WiseMappingException {
if (lock) {
this.lock(mindmap, user);
} else {
this.unlock(mindmap, user);
}
}
}

View File

@@ -62,4 +62,6 @@ public interface MindmapService {
MindMapHistory findMindmapHistory(int id, int hid) throws WiseMappingException;
void updateCollaboration(@NotNull Collaborator collaborator, @NotNull Collaboration collaboration) throws WiseMappingException;
LockManager getLockManager();
}

View File

@@ -45,6 +45,11 @@ public class MindmapServiceImpl
private NotificationService notificationService;
private String adminUser;
final private LockManager lockManager;
public MindmapServiceImpl() {
this.lockManager = new LockManagerImpl();
}
@Override
public boolean hasPermissions(@Nullable User user, int mapId, @NotNull CollaborationRole grantedRole) {
@@ -94,6 +99,11 @@ public class MindmapServiceImpl
if (mindMap.getTitle() == null || mindMap.getTitle().length() == 0) {
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);
}
@@ -264,6 +274,12 @@ public class MindmapServiceImpl
mindmapManager.updateCollaboration(collaboration);
}
@Override
@NotNull
public LockManager getLockManager() {
return this.lockManager;
}
private Collaboration getCollaborationBy(String email, Set<Collaboration> collaborations) {
Collaboration collaboration = null;

View File

@@ -249,7 +249,7 @@ DIRECT_LINK_EXPLANATION=Copy and paste the link below to share your map with col
TEMPORAL_PASSWORD_SENT=Your temporal password has been sent
TEMPORAL_PASSWORD_SENT_DETAILS=We've sent you an email that will allow you to reset your password. Please check your email now.
TEMPORAL_PASSWORD_SENT_SUPPORT=If you have any problem receiving the email, contact us to <a href\="mailto\:support@wisemapping.com">support@wisemapping.com </a>
MINDMAP_TIMESTAMP_OUTDATED=It's not possible to save your changes because your mindmap is out of date. Refresh the page and try again.

View File

@@ -1,8 +1,9 @@
log4j.rootLogger=WARN, stdout, R
log4j.logger.com.wisemapping.service.LockManager=DEBUG,stdout,R
log4j.logger.com.wisemapping=WARN,stdout,R
log4j.logger.org.springframework=WARN,stdout,R
log4j.logger.org.codehaus.jackson=WARN,stdout,R
log4j.logger.org.hibernate=DEBUG,stdout,R
log4j.logger.org.hibernate=WARN,stdout,R
log4j.logger.org.hibernate.SQL=true

View File

@@ -35,7 +35,7 @@
// Configure designer options ...
var options = loadDesignerOptions();
<c:if test="${!memoryPersistence}">
options.persistenceManager = new mindplot.RESTPersistenceManager("service/maps/{id}/document", "service/maps/{id}/history/latest");
options.persistenceManager = new mindplot.RESTPersistenceManager("service/maps/{id}/document", "service/maps/{id}/history/latest","service/maps/{id}/lock");
</c:if>
var userOptions = ${mindmap.properties};
options.zoom = userOptions.zoom;

View File

@@ -43,4 +43,4 @@
</node>
</node>
</node>
</map>
</map>