Commit 26d299fc authored by Peter Harrison's avatar Peter Harrison

Working primarily on getting unit tests working again.

May have resolved issue with Cache and Cached Lists.
parent 6818ebd4
......@@ -4,7 +4,7 @@
<groupId>nz.net.gravity</groupId>
<artifactId>gravity</artifactId>
<packaging>war</packaging>
<version>3.0.0</version>
<version>3.0.1-SNAPSHOT</version>
<name>Gravity Workflow Automation</name>
<properties>
......
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
*
* This file is part of Gravity Workflow Automation.
*
* Gravity Workflow Automation is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Gravity Workflow Automation 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gravity Workflow Automation.
* If not, see <http://www.gnu.org/licenses/>.
*/
package nz.net.orcon.kanban.automation;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import nz.net.orcon.kanban.model.Coup;
import nz.net.orcon.kanban.model.IdRequest;
import nz.net.orcon.kanban.model.IdResponse;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.scheduling.annotation.Scheduled;
/**
* The purpose of the Cluster manager is to manage all the functions that need to be
* coordinated across the Cluster.
*
* - Invalidation of Caches.
* - Phase Updates
* - Getting Identifiers.
* - Master Server Protocol
*
* @author peter
*
*/
public class ClusterManager {
private static final Logger LOG = LoggerFactory.getLogger(ClusterManager.class);
private boolean leader;
private long startTime = System.currentTimeMillis();
private String serverId = RandomStringUtils.random(10, true, true);
@Autowired
@Qualifier("idJmsTemplate")
private JmsTemplate idTemplate;
@Autowired
@Qualifier("idSyncReceiver")
private SyncReceive idSyncReceiever;
@Autowired
JcrObserver jcrObserver;
@Autowired
TimerManager timerManager;
public Long getId(String path, String field) throws Exception {
if( LOG.isDebugEnabled()){
LOG.debug("Generating ID from " + path + "." + field);
}
Message message = requestId(path,field);
if(message==null){
executeChallenge();
message = requestId(path,field);
}
ObjectMessage objectMessage = (ObjectMessage) message;
IdResponse idResponse = (IdResponse) objectMessage.getObject();
return idResponse.getId();
}
public interface ClusterManager {
public Message requestId(String path, String field){
String requestId = RandomStringUtils.random(10, true, true);
SyncMessage idSync = idSyncReceiever.register(requestId);
IdRequest request = new IdRequest();
request.setField(field);
request.setPath(path);
request.setRequestId(requestId);
idTemplate.convertAndSend(request);
return idSync.receiveMessage(10000l);
}
public Long getId(String path, String field) throws Exception;
public String getIdString(String path, String field, String prefix) throws Exception{
Long id = getId( path, field);
return prefix + id.toString();
}
/**
* Note that this method is simply a wrapper to eat the return value of executeChallenge.
* This is needed to be able to schedule the task.
*
* @throws Exception
*/
@Scheduled( fixedDelay=30000l )
public void pollLeader() throws Exception{
if(!isLeader()){
this.executeChallenge();
}
}
public synchronized boolean executeChallenge() throws Exception{
SyncMessage coupSync = idSyncReceiever.register(this.serverId);
Coup coup = new Coup();
coup.setRequestId(this.serverId);
coup.setCoupTime(this.startTime);
idTemplate.convertAndSend(coup);
if( LOG.isDebugEnabled()){
LOG.debug("Sending Heatbeat: " + this.serverId +", Time: " + coup.getCoupTime() + ", ID: " + coup.getRequestId());
}
Message coupMessage = coupSync.receiveMessage(10000l);
boolean newLeader = (coupMessage==null);
if( isLeader()!=newLeader){
if(newLeader){
LOG.warn("FAILOVER: Node promoted to MASTER.");
setLeader(true);
this.jcrObserver.start();
this.timerManager.startup();
} else {
LOG.warn("FAILOVER: Node demoted to worker.");
setLeader(false);
this.jcrObserver.stop();
this.timerManager.stopAll();
}
}
return isLeader();
}
public String getIdString(String path, String field, String prefix) throws Exception;
public synchronized void setLeader(boolean leader) {
this.leader = leader;
}
public void setLeader(boolean leader);
public synchronized boolean isLeader() {
return leader;
}
public boolean isLeader();
public long getStartTime() {
return startTime;
}
public String getServerId() {
return serverId;
}
public long getStartTime();
public String getServerId();
}
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
*
* This file is part of Gravity Workflow Automation.
*
* Gravity Workflow Automation is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Gravity Workflow Automation 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gravity Workflow Automation.
* If not, see <http://www.gnu.org/licenses/>.
*/
package nz.net.orcon.kanban.automation;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import nz.net.orcon.kanban.model.Coup;
import nz.net.orcon.kanban.model.IdRequest;
import nz.net.orcon.kanban.model.IdResponse;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.scheduling.annotation.Scheduled;
/**
* The purpose of the Cluster manager is to manage all the functions that need to be
* coordinated across the Cluster.
*
* - Invalidation of Caches.
* - Phase Updates
* - Getting Identifiers.
* - Master Server Protocol
*
* @author peter
*
*/
public class ClusterManagerImpl implements ClusterManager {
private static final Logger LOG = LoggerFactory.getLogger(ClusterManagerImpl.class);
private boolean leader;
private long startTime = System.currentTimeMillis();
private String serverId = RandomStringUtils.random(10, true, true);
@Autowired
@Qualifier("idJmsTemplate")
private JmsTemplate idTemplate;
@Autowired
@Qualifier("idSyncReceiver")
private SyncReceive idSyncReceiever;
@Autowired
JcrObserver jcrObserver;
@Autowired
TimerManager timerManager;
public Long getId(String path, String field) throws Exception {
if( LOG.isDebugEnabled()){
LOG.debug("Generating ID from " + path + "." + field);
}
Message message = requestId(path,field);
if(message==null){
executeChallenge();
message = requestId(path,field);
}
ObjectMessage objectMessage = (ObjectMessage) message;
IdResponse idResponse = (IdResponse) objectMessage.getObject();
return idResponse.getId();
}
private Message requestId(String path, String field){
String requestId = RandomStringUtils.random(10, true, true);
SyncMessage idSync = idSyncReceiever.register(requestId);
IdRequest request = new IdRequest();
request.setField(field);
request.setPath(path);
request.setRequestId(requestId);
idTemplate.convertAndSend(request);
return idSync.receiveMessage(10000l);
}
public String getIdString(String path, String field, String prefix) throws Exception{
Long id = getId( path, field);
return prefix + id.toString();
}
/**
* Note that this method is simply a wrapper to eat the return value of executeChallenge.
* This is needed to be able to schedule the task.
*
* @throws Exception
*/
@Scheduled( fixedDelay=30000l )
public void pollLeader() throws Exception{
if(!isLeader()){
this.executeChallenge();
}
}
public synchronized boolean executeChallenge() throws Exception{
SyncMessage coupSync = idSyncReceiever.register(this.serverId);
Coup coup = new Coup();
coup.setRequestId(this.serverId);
coup.setCoupTime(this.startTime);
idTemplate.convertAndSend(coup);
if( LOG.isDebugEnabled()){
LOG.debug("Sending Heatbeat: " + this.serverId +", Time: " + coup.getCoupTime() + ", ID: " + coup.getRequestId());
}
Message coupMessage = coupSync.receiveMessage(10000l);
boolean newLeader = (coupMessage==null);
if( isLeader()!=newLeader){
if(newLeader){
LOG.warn("FAILOVER: Node promoted to MASTER.");
setLeader(true);
this.jcrObserver.start();
this.timerManager.startup();
} else {
LOG.warn("FAILOVER: Node demoted to worker.");
setLeader(false);
this.jcrObserver.stop();
this.timerManager.stopAll();
}
}
return isLeader();
}
public synchronized void setLeader(boolean leader) {
this.leader = leader;
}
public synchronized boolean isLeader() {
return leader;
}
public long getStartTime() {
return startTime;
}
public String getServerId() {
return serverId;
}
}
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
* (C) Copyright 2016 Peter Harrison
*
* This file is part of Gravity Workflow Automation.
*
......@@ -21,8 +22,6 @@
package nz.net.orcon.kanban.automation;
import java.net.UnknownHostException;
import javax.annotation.PreDestroy;
import nz.net.orcon.kanban.tools.OcmMapperFactory;
......@@ -33,9 +32,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.jcr.LoginException;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.observation.ObservationManager;
public class JcrObserver {
......@@ -50,7 +46,7 @@ public class JcrObserver {
ObjectContentManager ocm;
public void start() throws UnsupportedRepositoryOperationException, LoginException, RepositoryException, ClassNotFoundException, UnknownHostException{
public void start() throws Exception{
this.ocm = ocmFactory.getOcm();
ObservationManager observationManager = ocm.getSession().getWorkspace().getObservationManager();
final String[] types = { "nt:unstructured" };
......
......@@ -49,15 +49,15 @@ public class BoardsCache extends CacheImpl<Board> implements MessageListener {
@Autowired
@Qualifier("phaseChangeJmsTemplate")
JmsTemplate jmsTemplate;
private JmsTemplate jmsTemplate;
private final Object lock = new Object();
@Resource(name="ocmFactory")
OcmMapperFactory ocmFactory;
private OcmMapperFactory ocmFactory;
@Autowired
ListTools listTools;
private ListTools listTools;
public void incrementCardCount( String boardId, String phaseId, int increment) {
PhaseChange change = new PhaseChange( boardId, phaseId, increment);
......
......@@ -41,12 +41,11 @@ abstract public class CacheImpl<T> implements Cache<T> {
if(itemIds.length>1){
String[] listId = new String[itemIds.length-1];
for( int a=0; a<itemIds.length; a++){
for( int a=0; a<itemIds.length-1; a++){
listId[a] = itemIds[a];
}
this.cacheList.remove(getCacheId(listId));
}
}
@Override
......
......@@ -108,8 +108,8 @@ public class CardController {
@Autowired
CardListener cardListener;
@Autowired
AutomationEngine automationEngine;
//@Autowired
//AutomationEngine automationEngine;
@Autowired
private NotificationController notificationController;
......@@ -177,8 +177,9 @@ public class CardController {
CardHolder cardHolder = new CardHolder();
cardHolder.setBoardId(boardId);
cardHolder.setCardId(cardId);
Map<String, Map<String,Boolean>> explain = automationEngine.explain(cardHolder);
return explain;
//Map<String, Map<String,Boolean>> explain = automationEngine.explain(cardHolder);
//return explain;
return null;
}
@PreAuthorize("hasPermission(#boardId, 'BOARD', 'READ,WRITE,ADMIN')")
......
......@@ -138,7 +138,7 @@ public class NotificationController {
}
@RequestMapping(value = "/{notificationType}", method=RequestMethod.GET)
public @ResponseBody NotificationTypeMapping getNotificationTypeMapping(@PathVariable String notificationType) throws LoginException, RepositoryException, ClassNotFoundException, UnknownHostException{
public @ResponseBody NotificationTypeMapping getNotificationTypeMapping(@PathVariable String notificationType) throws Exception{
ObjectContentManager ocm = ocmFactory.getOcm();
NotificationTypeMapping notificationTypeMapping = null;
try{
......
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
*
* This file is part of Gravity Workflow Automation.
*
* Gravity Workflow Automation is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Gravity Workflow Automation 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gravity Workflow Automation.
* If not, see <http://www.gnu.org/licenses/>.
*/
package nz.net.orcon.kanban.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import nz.net.orcon.kanban.controllers.TeamCache;
import nz.net.orcon.kanban.model.Team;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
public class SecurityTool {
private static final Logger LOG = LoggerFactory.getLogger(SecurityTool.class);
public interface SecurityTool {
public static final String SYSTEM = "system";
@Autowired
TeamCache teamCache;
public String getCurrentUser() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
Object principal = SYSTEM;
if(authentication!=null){
principal = authentication.getPrincipal();
}
return (String) principal;
}
public void iAmSystem() throws Exception {
SecurityContext context = SecurityContextHolder.getContext();
Collection<? extends GrantedAuthority> authorities = this.getRoles(SYSTEM);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(SYSTEM, "", authorities);
context.setAuthentication(authentication);
}
public abstract String getCurrentUser();
public abstract void iAmSystem() throws Exception;
/**
* Determine if the user is authorised based on the supplied roles.
* @param roles
* @param filter
* @return boolean - is the user authorised?
*/
public boolean isAuthorised( Map<String,String> roles, String filter){
if( roles==null ){
return false;
}
SecurityContext context = SecurityContextHolder.getContext();
if(context==null || context.getAuthentication()==null){
return false;
}
String username = (String) context.getAuthentication().getPrincipal();
Set<String> teams = new HashSet<String>();
for(GrantedAuthority authority : context.getAuthentication().getAuthorities()){
teams.add(authority.getAuthority());
}
for( String authorised : roles.keySet()){
if(filter==null || filter.contains(( roles.get(authorised)))){
if(username.equals(authorised)){
return true;
}
if(teams.contains(authorised)){
return true;
}
}
}
LOG.warn("Unauthorized: " + username);
return false;
}
public List<GrantedAuthority> getRoles(String userName) throws Exception {
List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
Map<String, String> listTeams = teamCache.list();
//get users of team and then compare to find out teams where user belongs.
Set<String> keySet = listTeams.keySet();
for (Iterator<String> iterator = keySet.iterator(); iterator.hasNext();) {
String teamId = iterator.next();
Team team = teamCache.getItem(teamId);
if(team.getMembers().containsKey(userName)){
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(teamId);
roles.add(grantedAuthority);
}
}
return roles;
}
public Map<String,String> initRole(Map<String,String> roles ){
public abstract boolean isAuthorised(Map<String, String> roles,
String filter);
public abstract List<GrantedAuthority> getRoles(String userName)
throws Exception;
public abstract Map<String, String> initRole(Map<String, String> roles);
if( roles==null){
roles = new HashMap<String,String>();
}
if( roles.get("administrators")==null){
roles.put("administrators", "ADMIN");
}
String username = getCurrentUser();
if( roles.get(username)==null){
roles.put(username, "ADMIN");
}
return roles;
}
}
}
\ No newline at end of file
/**
* GRAVITY WORKFLOW AUTOMATION
* (C) Copyright 2015 Orcon Limited
*
* This file is part of Gravity Workflow Automation.
*
* Gravity Workflow Automation is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Gravity Workflow Automation 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Gravity Workflow Automation.
* If not, see <http://www.gnu.org/licenses/>.
*/
package nz.net.orcon.kanban.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import nz.net.orcon.kanban.controllers.TeamCache;
import nz.net.orcon.kanban.model.Team;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
public class SecurityToolImpl implements SecurityTool {
private static final Logger LOG = LoggerFactory.getLogger(SecurityToolImpl.class);
@Autowired
TeamCache teamCache;