Merged V2.2 to HEAD

10931: Merged V2.1 to V2.2
      9931: Fix for https://issues.alfresco.com/jira/browse/ETWOONE-295
      10094: Further fix for ETWOONE-241: SAXException - XML parser apparently is not thread safe
      10101: Resolve ACT 1282: wcm workflow falling over on Oracle while hitting in clause limit of 1000 expressions.
      10188: https://issues.alfresco.com/jira/browse/ETWOONE-74 (Part 1)
      10447: ETWOONE-328: performance improvement added to rule trigger code
      10455: Fix for ETWOONE-306.
      10292: Fix for ETWOONE-92: If two users update the same contents at the same time, you get InvalidNodeRefException
      10293: Fix for ETWOONE-116: Send email action does not handle invalid email address
      10294: Fix for ETWOONE-164: when a powerpoint 2007 pptx is stored in alfresco ...
      10341: Action Evaluator request level cache


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@10934 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2008-09-19 12:51:08 +00:00
parent 3ca446e81e
commit 8fb3a02d1f
13 changed files with 556 additions and 103 deletions

View File

@@ -411,6 +411,12 @@
<!-- Are tickets only valid for a single use? --> <!-- Are tickets only valid for a single use? -->
<property name="oneOff"> <property name="oneOff">
<value>false</value> <value>false</value>
</property>
<!-- If ticketsEpire is true then how they should expire -->
<!-- AFTER_INACTIVITY, AFTER_FIXED_TIME, DO_NOT_EXPIRE -->
<!-- The default is AFTER_FIXED_TIME -->
<property name="expiryMode">
<value>AFTER_FIXED_TIME</value>
</property> </property>
</bean> </bean>

View File

@@ -421,6 +421,12 @@
<ref bean="admLuceneIndexerAndSearcherFactory" /> <ref bean="admLuceneIndexerAndSearcherFactory" />
</property> </property>
</bean> </bean>
<bean id="versionSearchService" class="org.alfresco.repo.search.SearcherComponent">
<property name="indexerAndSearcherFactory">
<ref bean="admLuceneUnIndexedIndexerAndSearcherFactory" />
</property>
</bean>
<!-- --> <!-- -->
@@ -438,13 +444,17 @@
<entry key="workspace"> <entry key="workspace">
<ref bean="admLuceneIndexerAndSearcherFactory"></ref> <ref bean="admLuceneIndexerAndSearcherFactory"></ref>
</entry> </entry>
<entry key="versionStore">
<ref bean="admLuceneIndexerAndSearcherFactory"></ref>
</entry>
<entry key="avm"> <entry key="avm">
<ref bean="avmLuceneIndexerAndSearcherFactory"></ref> <ref bean="avmLuceneIndexerAndSearcherFactory"></ref>
</entry> </entry>
</map> </map>
</property>
<property name="redirectedStoreBindings">
<map>
<entry key="workspace://lightWeightVersionStore">
<ref bean="admLuceneUnIndexedIndexerAndSearcherFactory"></ref>
</entry>
</map>
</property> </property>
</bean> </bean>
@@ -544,6 +554,63 @@
<ref bean="hibernateL1CacheBulkLoader"></ref> <ref bean="hibernateL1CacheBulkLoader"></ref>
</property> </property>
</bean> </bean>
<!-- Indexer and searchers for lucene -->
<bean id="admLuceneUnIndexedIndexerAndSearcherFactory"
class="org.alfresco.repo.search.impl.lucene.ADMLuceneUnIndexedIndexAndSearcherFactory">
<property name="nodeService">
<ref bean="mlAwareNodeService" />
</property>
<property name="dictionaryService">
<ref bean="dictionaryService" />
</property>
<property name="nameSpaceService">
<ref bean="namespaceService" />
</property>
<property name="indexRootLocation">
<value>${dir.indexes}</value>
</property>
<property name="contentService">
<ref bean="contentService" />
</property>
<property name="queryRegister">
<ref bean="queryRegisterComponent" />
</property>
<property name="maxAtomicTransformationTime">
<value>${lucene.maxAtomicTransformationTime}</value> <!-- milliseconds allowed for inline text transformation -->
</property>
<property name="queryMaxClauses">
<value>${lucene.query.maxClauses}</value>
</property>
<property name="indexerBatchSize">
<value>${lucene.indexer.batchSize}</value>
</property>
<property name="lockDirectory">
<value>${dir.indexes.lock}</value>
</property>
<property name="indexerMaxFieldLength">
<value>${lucene.indexer.maxFieldLength}</value>
</property>
<property name="writeLockTimeout">
<value>${lucene.write.lock.timeout}</value>
</property>
<property name="commitLockTimeout">
<value>${lucene.commit.lock.timeout}</value>
</property>
<property name="lockPollInterval">
<value>${lucene.lock.poll.interval}</value>
</property>
<property name="defaultMLIndexAnalysisMode">
<value>EXACT_LANGUAGE_AND_ALL</value>
</property>
<property name="defaultMLSearchAnalysisMode">
<value>EXACT_LANGUAGE_AND_ALL</value>
</property>
<property name="threadPoolExecutor">
<ref bean="indexThreadPoolExecutor"></ref>
</property>
</bean>
<!-- Indexer and searchers for lucene --> <!-- Indexer and searchers for lucene -->
<bean id="avmLuceneIndexerAndSearcherFactory" <bean id="avmLuceneIndexerAndSearcherFactory"
@@ -677,7 +744,7 @@
<ref bean="versionCounterService" /> <ref bean="versionCounterService" />
</property> </property>
<property name="searcher"> <property name="searcher">
<ref bean="admSearchService" /> <ref bean="versionSearchService" />
</property> </property>
<property name="dictionaryService"> <property name="dictionaryService">
<ref bean="dictionaryService" /> <ref bean="dictionaryService" />

View File

@@ -34,7 +34,6 @@ import java.util.Set;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.template.DateCompareMethod; import org.alfresco.repo.template.DateCompareMethod;
@@ -54,6 +53,7 @@ import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.EmailValidator;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.mail.javamail.MimeMessageHelper;
@@ -261,7 +261,7 @@ public class MailActionExecuter extends ActionExecuterAbstractBase
{ {
NodeRef person = personService.getPerson(authority); NodeRef person = personService.getPerson(authority);
String address = (String)nodeService.getProperty(person, ContentModel.PROP_EMAIL); String address = (String)nodeService.getProperty(person, ContentModel.PROP_EMAIL);
if (address != null && address.length() != 0) if (address != null && address.length() != 0 && validateAddress(address))
{ {
recipients.add(address); recipients.add(address);
} }
@@ -351,6 +351,28 @@ public class MailActionExecuter extends ActionExecuterAbstractBase
logger.error("Failed to send email to " + to, e); logger.error("Failed to send email to " + to, e);
} }
} }
/**
* Return true if address has valid format
* @param address
* @return
*/
private boolean validateAddress(String address)
{
boolean result = false;
EmailValidator emailValidator = EmailValidator.getInstance();
if (emailValidator.isValid(address))
{
result = true;
}
else
{
logger.error("Failed to send email to '" + address + "' as the address is incorrectly formatted" );
}
return result;
}
/** /**
* @param ref The node representing the current document ref * @param ref The node representing the current document ref

View File

@@ -538,7 +538,8 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
* @param ruleNodeRef the rule node reference * @param ruleNodeRef the rule node reference
* @return the rule * @return the rule
*/ */
public Rule getRule(NodeRef ruleNodeRef) @SuppressWarnings("unchecked")
public Rule getRule(NodeRef ruleNodeRef)
{ {
// Get the rule properties // Get the rule properties
Map<QName, Serializable> props = this.runtimeNodeService.getProperties(ruleNodeRef); Map<QName, Serializable> props = this.runtimeNodeService.getProperties(ruleNodeRef);

View File

@@ -129,14 +129,14 @@ public class RuleTypeImpl extends CommonResourceAbstractBase implements RuleType
this.nodeService.exists(actionedUponNodeRef) == true && this.nodeService.exists(actionedUponNodeRef) == true &&
this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_TEMPORARY) == false) this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_TEMPORARY) == false)
{ {
if (this.ruleService.hasRules(nodeRef) == true) List<Rule> rules = this.ruleService.getRules(
nodeRef,
true,
this.name);
if (rules.size() != 0)
{ {
List<Rule> rules = this.ruleService.getRules( for (Rule rule : rules)
nodeRef,
true,
this.name);
for (Rule rule : rules)
{ {
if (logger.isDebugEnabled() == true) if (logger.isDebugEnabled() == true)
{ {

View File

@@ -42,15 +42,15 @@ import org.alfresco.service.namespace.NamespaceService;
*/ */
public class ADMLuceneIndexerAndSearcherFactory extends AbstractLuceneIndexerAndSearcherFactory implements SupportsBackgroundIndexing public class ADMLuceneIndexerAndSearcherFactory extends AbstractLuceneIndexerAndSearcherFactory implements SupportsBackgroundIndexing
{ {
private DictionaryService dictionaryService; protected DictionaryService dictionaryService;
private NamespaceService nameSpaceService; private NamespaceService nameSpaceService;
private NodeService nodeService; protected NodeService nodeService;
private FullTextSearchIndexer fullTextSearchIndexer; protected FullTextSearchIndexer fullTextSearchIndexer;
private ContentService contentService; protected ContentService contentService;
/** /**
* Set the dictinary service * Set the dictinary service

View File

@@ -393,6 +393,18 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl<NodeRef> imp
indexer.initialise(storeRef, deltaId); indexer.initialise(storeRef, deltaId);
return indexer; return indexer;
} }
public static ADMLuceneNoActionIndexerImpl getNoActionIndexer(StoreRef storeRef, String deltaId, LuceneConfig config) throws LuceneIndexException
{
if (s_logger.isDebugEnabled())
{
s_logger.debug("Creating indexer");
}
ADMLuceneNoActionIndexerImpl indexer = new ADMLuceneNoActionIndexerImpl();
indexer.setLuceneConfig(config);
indexer.initialise(storeRef, deltaId);
return indexer;
}
/* /*
* Transactional support Used by the resource manager for indexers. * Transactional support Used by the resource manager for indexers.

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program 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 2
* of the License, or (at your option) any later version.
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.search.impl.lucene;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
public class ADMLuceneNoActionIndexerImpl extends ADMLuceneIndexerImpl
{
public ADMLuceneNoActionIndexerImpl()
{
// TODO Auto-generated constructor stub
}
@Override
public void createChildRelationship(ChildAssociationRef relationshipRef) throws LuceneIndexException
{
return;
}
@Override
public void createNode(ChildAssociationRef relationshipRef) throws LuceneIndexException
{
NodeRef childRef = relationshipRef.getChildRef();
// If we have the root node we delete all other root nodes first
if ((relationshipRef.getParentRef() == null) && childRef.equals(nodeService.getRootNode(childRef.getStoreRef())))
{
// do the root node only
super.createNode(relationshipRef);
}
else
{
// Nothing
}
}
@Override
public void deleteChildRelationship(ChildAssociationRef relationshipRef) throws LuceneIndexException
{
return;
}
@Override
public void deleteNode(ChildAssociationRef relationshipRef) throws LuceneIndexException
{
NodeRef childRef = relationshipRef.getChildRef();
// If we have the root node we delete all other root nodes first
if ((relationshipRef.getParentRef() == null) && childRef.equals(nodeService.getRootNode(childRef.getStoreRef())))
{
// do the root node only
super.deleteNode(relationshipRef);
}
else
{
// Nothing
}
}
@Override
public void updateChildRelationship(ChildAssociationRef relationshipBeforeRef, ChildAssociationRef relationshipAfterRef) throws LuceneIndexException
{
return;
}
@Override
public void updateNode(NodeRef nodeRef) throws LuceneIndexException
{
if((nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ROOT) && nodeRef.equals(nodeService.getRootNode(nodeRef.getStoreRef()))))
{
super.updateNode(nodeRef);
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program 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 2
* of the License, or (at your option) any later version.
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.search.impl.lucene;
import org.alfresco.service.cmr.repository.StoreRef;
public class ADMLuceneUnIndexedIndexAndSearcherFactory extends ADMLuceneIndexerAndSearcherFactory
{
@Override
protected LuceneIndexer createIndexer(StoreRef storeRef, String deltaId)
{
ADMLuceneNoActionIndexerImpl indexer = ADMLuceneIndexerImpl.getNoActionIndexer(storeRef, deltaId, this);
indexer.setNodeService(nodeService);
indexer.setDictionaryService(dictionaryService);
// indexer.setLuceneIndexLock(luceneIndexLock);
indexer.setFullTextSearchIndexer(fullTextSearchIndexer);
indexer.setContentService(contentService);
indexer.setMaxAtomicTransformationTime(getMaxTransformationTime());
return indexer;
}
}

View File

@@ -48,6 +48,7 @@ import net.sf.acegisecurity.providers.dao.SaltSource;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.ExpiryMode;
import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.Ticket; import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.Ticket;
import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
@@ -139,8 +140,7 @@ public class AuthenticationTest extends TestCase
authenticationManager = (AuthenticationManager) ctx.getBean("authenticationManager"); authenticationManager = (AuthenticationManager) ctx.getBean("authenticationManager");
saltSource = (SaltSource) ctx.getBean("saltSource"); saltSource = (SaltSource) ctx.getBean("saltSource");
TransactionService transactionService = (TransactionService) ctx.getBean(ServiceRegistry.TRANSACTION_SERVICE TransactionService transactionService = (TransactionService) ctx.getBean(ServiceRegistry.TRANSACTION_SERVICE.getLocalName());
.getLocalName());
userTransaction = transactionService.getUserTransaction(); userTransaction = transactionService.getUserTransaction();
userTransaction.begin(); userTransaction.begin();
@@ -155,8 +155,7 @@ public class AuthenticationTest extends TestCase
systemNodeRef = nodeService.createNode(rootNodeRef, children, system, container).getChildRef(); systemNodeRef = nodeService.createNode(rootNodeRef, children, system, container).getChildRef();
typesNodeRef = nodeService.createNode(systemNodeRef, children, types, container).getChildRef(); typesNodeRef = nodeService.createNode(systemNodeRef, children, types, container).getChildRef();
Map<QName, Serializable> props = createPersonProperties("Andy"); Map<QName, Serializable> props = createPersonProperties("Andy");
personAndyNodeRef = nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, props) personAndyNodeRef = nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, props).getChildRef();
.getChildRef();
assertNotNull(personAndyNodeRef); assertNotNull(personAndyNodeRef);
deleteAndy(); deleteAndy();
@@ -270,6 +269,10 @@ public class AuthenticationTest extends TestCase
authenticationService.authenticate("Andy_ Woof/Domain", "".toCharArray()); authenticationService.authenticate("Andy_ Woof/Domain", "".toCharArray());
assertEquals("Andy_ Woof/Domain", authenticationService.getCurrentUserName()); assertEquals("Andy_ Woof/Domain", authenticationService.getCurrentUserName());
authenticationService.createAuthentication("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?\\|", "".toCharArray());
authenticationService.authenticate("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?\\|", "".toCharArray());
assertEquals("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?\\|", authenticationService.getCurrentUserName());
if (! tenantService.isEnabled()) if (! tenantService.isEnabled())
{ {
authenticationService.createAuthentication("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?\\|", "".toCharArray()); authenticationService.createAuthentication("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?\\|", "".toCharArray());
@@ -307,8 +310,7 @@ public class AuthenticationTest extends TestCase
assertTrue(AndyDetails.isCredentialsNonExpired()); assertTrue(AndyDetails.isCredentialsNonExpired());
assertTrue(AndyDetails.isEnabled()); assertTrue(AndyDetails.isEnabled());
assertNotSame("cabbage", AndyDetails.getPassword()); assertNotSame("cabbage", AndyDetails.getPassword());
assertEquals(AndyDetails.getPassword(), passwordEncoder.encodePassword("cabbage", saltSource assertEquals(AndyDetails.getPassword(), passwordEncoder.encodePassword("cabbage", saltSource.getSalt(AndyDetails)));
.getSalt(AndyDetails)));
assertEquals(1, AndyDetails.getAuthorities().length); assertEquals(1, AndyDetails.getAuthorities().length);
// Object oldSalt = dao.getSalt(AndyDetails); // Object oldSalt = dao.getSalt(AndyDetails);
@@ -465,8 +467,6 @@ public class AuthenticationTest extends TestCase
// assertNull(dao.getUserOrNull("Andy")); // assertNull(dao.getUserOrNull("Andy"));
} }
public void testTicket() public void testTicket()
{ {
dao.createUser("Andy", "ticket".toCharArray()); dao.createUser("Andy", "ticket".toCharArray());
@@ -568,6 +568,147 @@ public class AuthenticationTest extends TestCase
// assertNull(dao.getUserOrNull("Andy")); // assertNull(dao.getUserOrNull("Andy"));
} }
public void testTicketExpiryMode()
{
InMemoryTicketComponentImpl tc = new InMemoryTicketComponentImpl();
tc.setOneOff(false);
tc.setTicketsExpire(true);
tc.setValidDuration("P5S");
tc.setTicketsCache(ticketsCache);
tc.setExpiryMode(ExpiryMode.AFTER_FIXED_TIME.toString());
dao.createUser("Andy", "ticket".toCharArray());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Andy", "ticket");
token.setAuthenticated(false);
Authentication result = authenticationManager.authenticate(token);
result.setAuthenticated(true);
String ticket = tc.getNewTicket(getUserName(result));
tc.validateTicket(ticket);
assertEquals(ticketComponent.getCurrentTicket("Andy"), ticket);
tc.validateTicket(ticket);
assertEquals(ticketComponent.getCurrentTicket("Andy"), ticket);
tc.validateTicket(ticket);
assertEquals(ticketComponent.getCurrentTicket("Andy"), ticket);
synchronized (this)
{
try
{
wait(10000);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try
{
tc.validateTicket(ticket);
assertNotNull(null);
}
catch (AuthenticationException e)
{
}
try
{
tc.validateTicket(ticket);
assertNotNull(null);
}
catch (AuthenticationException e)
{
}
try
{
tc.validateTicket(ticket);
assertNotNull(null);
}
catch (AuthenticationException e)
{
}
synchronized (this)
{
try
{
wait(10000);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try
{
tc.validateTicket(ticket);
assertNotNull(null);
}
catch (AuthenticationException e)
{
}
tc.setExpiryMode(ExpiryMode.AFTER_INACTIVITY.toString());
ticket = tc.getNewTicket(getUserName(result));
for (int i = 0; i < 50; i++)
{
synchronized (this)
{
try
{
wait(100);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
tc.validateTicket(ticket);
}
}
synchronized (this)
{
try
{
wait(10000);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try
{
tc.validateTicket(ticket);
assertNotNull(null);
}
catch (AuthenticationException e)
{
}
dao.deleteUser("Andy");
// assertNull(dao.getUserOrNull("Andy"));
}
public void testTicketExpires() public void testTicketExpires()
{ {
InMemoryTicketComponentImpl tc = new InMemoryTicketComponentImpl(); InMemoryTicketComponentImpl tc = new InMemoryTicketComponentImpl();
@@ -704,7 +845,7 @@ public class AuthenticationTest extends TestCase
// assertNull(dao.getUserOrNull("Andy")); // assertNull(dao.getUserOrNull("Andy"));
} }
public void testAuthenticationServiceGetNewTicket() public void testAuthenticationServiceGetNewTicket()
{ {
authenticationService.createAuthentication("GUEST", "".toCharArray()); authenticationService.createAuthentication("GUEST", "".toCharArray());
@@ -718,16 +859,16 @@ public class AuthenticationTest extends TestCase
// assert the user is authenticated // assert the user is authenticated
assertEquals("Andy", authenticationService.getCurrentUserName()); assertEquals("Andy", authenticationService.getCurrentUserName());
String ticket1 = authenticationService.getCurrentTicket(); String ticket1 = authenticationService.getCurrentTicket();
authenticationService.authenticate("Andy", "auth1".toCharArray()); authenticationService.authenticate("Andy", "auth1".toCharArray());
// assert the user is authenticated // assert the user is authenticated
assertEquals("Andy", authenticationService.getCurrentUserName()); assertEquals("Andy", authenticationService.getCurrentUserName());
String ticket2 = authenticationService.getCurrentTicket(); String ticket2 = authenticationService.getCurrentTicket();
assertFalse(ticket1.equals(ticket2)); assertFalse(ticket1.equals(ticket2));
} }

View File

@@ -49,7 +49,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
* Ticket prefix * Ticket prefix
*/ */
public static final String GRANTED_AUTHORITY_TICKET_PREFIX = "TICKET_"; public static final String GRANTED_AUTHORITY_TICKET_PREFIX = "TICKET_";
private static ThreadLocal<String> currentTicket = new ThreadLocal<String>(); private static ThreadLocal<String> currentTicket = new ThreadLocal<String>();
private boolean ticketsExpire; private boolean ticketsExpire;
@@ -61,10 +61,11 @@ public class InMemoryTicketComponentImpl implements TicketComponent
private String guid; private String guid;
private SimpleCache<String, Ticket> ticketsCache; // Can't use Ticket as it's private private SimpleCache<String, Ticket> ticketsCache; // Can't use Ticket as it's private
private ExpiryMode expiryMode = ExpiryMode.AFTER_FIXED_TIME;
/** /**
* IOC constructor * IOC constructor
*
*/ */
public InMemoryTicketComponentImpl() public InMemoryTicketComponentImpl()
{ {
@@ -89,7 +90,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
{ {
expiryDate = Duration.add(new Date(), validDuration); expiryDate = Duration.add(new Date(), validDuration);
} }
Ticket ticket = new Ticket(ticketsExpire, expiryDate, userName); Ticket ticket = new Ticket(ticketsExpire ? expiryMode : ExpiryMode.DO_NOT_EXPIRE, expiryDate, userName, validDuration);
ticketsCache.put(ticket.getTicketId(), ticket); ticketsCache.put(ticket.getTicketId(), ticket);
String ticketString = GRANTED_AUTHORITY_TICKET_PREFIX + ticket.getTicketId(); String ticketString = GRANTED_AUTHORITY_TICKET_PREFIX + ticket.getTicketId();
currentTicket.set(ticketString); currentTicket.set(ticketString);
@@ -119,6 +120,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
/** /**
* Helper method to find a ticket * Helper method to find a ticket
*
* @param ticketString * @param ticketString
* @return - the ticket * @return - the ticket
*/ */
@@ -129,7 +131,8 @@ public class InMemoryTicketComponentImpl implements TicketComponent
} }
/** /**
* Helper method to extract the ticket id from the ticket string * Helper method to extract the ticket id from the ticket string
*
* @param ticketString * @param ticketString
* @return - the ticket key * @return - the ticket key
*/ */
@@ -271,14 +274,14 @@ public class InMemoryTicketComponentImpl implements TicketComponent
/** /**
* Ticket * Ticket
*
* @author andyh * @author andyh
*
*/ */
public static class Ticket implements Serializable public static class Ticket implements Serializable
{ {
private static final long serialVersionUID = -5904510560161261049L; private static final long serialVersionUID = -5904510560161261049L;
private boolean expires; private ExpiryMode expires;
private Date expiryDate; private Date expiryDate;
@@ -287,18 +290,18 @@ public class InMemoryTicketComponentImpl implements TicketComponent
private String ticketId; private String ticketId;
private String guid; private String guid;
private Duration validDuration;
Ticket(boolean expires, Date expiryDate, String userName) Ticket(ExpiryMode expires, Date expiryDate, String userName, Duration validDuration)
{ {
this.expires = expires; this.expires = expires;
this.expiryDate = expiryDate; this.expiryDate = expiryDate;
this.userName = userName; this.userName = userName;
this.validDuration = validDuration;
this.guid = UUIDGenerator.getInstance().generateRandomBasedUUID().toString(); this.guid = UUIDGenerator.getInstance().generateRandomBasedUUID().toString();
String encode = (expires ? "T" : "F") + String encode = (expires.toString()) + ((expiryDate == null) ? new Date().toString() : expiryDate.toString()) + userName + guid;
((expiryDate == null) ? new Date().toString(): expiryDate.toString()) +
userName + guid;
MessageDigest digester; MessageDigest digester;
try try
{ {
@@ -314,18 +317,18 @@ public class InMemoryTicketComponentImpl implements TicketComponent
} }
catch (NoSuchAlgorithmException e1) catch (NoSuchAlgorithmException e1)
{ {
CRC32 crc = new CRC32(); CRC32 crc = new CRC32();
crc.update(encode.getBytes()); crc.update(encode.getBytes());
byte[] bytes = new byte[4]; byte[] bytes = new byte[4];
long value = crc.getValue(); long value = crc.getValue();
bytes[0] = (byte)(value & 0xFF); bytes[0] = (byte) (value & 0xFF);
value >>>= 4; value >>>= 4;
bytes[1] = (byte)(value & 0xFF); bytes[1] = (byte) (value & 0xFF);
value >>>= 4; value >>>= 4;
bytes[2] = (byte)(value & 0xFF); bytes[2] = (byte) (value & 0xFF);
value >>>= 4; value >>>= 4;
bytes[3] = (byte)(value & 0xFF); bytes[3] = (byte) (value & 0xFF);
this.ticketId = new String(Hex.encodeHex(bytes)); this.ticketId = new String(Hex.encodeHex(bytes));
} }
} }
} }
@@ -337,14 +340,33 @@ public class InMemoryTicketComponentImpl implements TicketComponent
*/ */
boolean hasExpired() boolean hasExpired()
{ {
if (expires && (expiryDate != null) && (expiryDate.compareTo(new Date()) < 0)) Date now = new Date();
{ switch (expires)
return true;
}
else
{ {
case AFTER_FIXED_TIME:
if ((expiryDate != null) && (expiryDate.compareTo(now) < 0))
{
return true;
}
else
{
return false;
}
case AFTER_INACTIVITY:
if ((expiryDate != null) && (expiryDate.compareTo(now) < 0))
{
return true;
}
else
{
expiryDate = Duration.add(now, validDuration);
return false;
}
case DO_NOT_EXPIRE:
default:
return false; return false;
} }
} }
public boolean equals(Object o) public boolean equals(Object o)
@@ -358,9 +380,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
return false; return false;
} }
Ticket t = (Ticket) o; Ticket t = (Ticket) o;
return (this.expires == t.expires) return (this.expires == t.expires) && this.expiryDate.equals(t.expiryDate) && this.userName.equals(t.userName) && this.ticketId.equals(t.ticketId);
&& this.expiryDate.equals(t.expiryDate) && this.userName.equals(t.userName)
&& this.ticketId.equals(t.ticketId);
} }
public int hashCode() public int hashCode()
@@ -368,7 +388,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
return ticketId.hashCode(); return ticketId.hashCode();
} }
protected boolean getExpires() protected ExpiryMode getExpires()
{ {
return expires; return expires;
} }
@@ -390,10 +410,11 @@ public class InMemoryTicketComponentImpl implements TicketComponent
} }
/** /**
* Are tickets single use * Are tickets single use
* @param oneOff *
*/ * @param oneOff
*/
public void setOneOff(boolean oneOff) public void setOneOff(boolean oneOff)
{ {
this.oneOff = oneOff; this.oneOff = oneOff;
@@ -401,6 +422,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
/** /**
* Do tickets expire * Do tickets expire
*
* @param ticketsExpire * @param ticketsExpire
*/ */
public void setTicketsExpire(boolean ticketsExpire) public void setTicketsExpire(boolean ticketsExpire)
@@ -408,8 +430,19 @@ public class InMemoryTicketComponentImpl implements TicketComponent
this.ticketsExpire = ticketsExpire; this.ticketsExpire = ticketsExpire;
} }
/**
* How should tickets expire.
* @param exipryMode
*/
public void setExpiryMode(String expiryMode)
{
this.expiryMode = ExpiryMode.valueOf(expiryMode);
}
/** /**
* How long are tickets valid (XML duration as a string) * How long are tickets valid (XML duration as a string)
*
* @param validDuration * @param validDuration
*/ */
public void setValidDuration(String validDuration) public void setValidDuration(String validDuration)
@@ -430,12 +463,12 @@ public class InMemoryTicketComponentImpl implements TicketComponent
public String getCurrentTicket(String userName) public String getCurrentTicket(String userName)
{ {
String ticket = currentTicket.get(); String ticket = currentTicket.get();
if(ticket == null) if (ticket == null)
{ {
return getNewTicket(userName); return getNewTicket(userName);
} }
String ticketUser = getAuthorityForTicket(ticket); String ticketUser = getAuthorityForTicket(ticket);
if(userName.equals(ticketUser)) if (userName.equals(ticketUser))
{ {
return ticket; return ticket;
} }
@@ -449,9 +482,14 @@ public class InMemoryTicketComponentImpl implements TicketComponent
{ {
clearCurrentSecurityContext(); clearCurrentSecurityContext();
} }
public static void clearCurrentSecurityContext() public static void clearCurrentSecurityContext()
{ {
currentTicket.set(null); currentTicket.set(null);
} }
public enum ExpiryMode
{
AFTER_INACTIVITY, AFTER_FIXED_TIME, DO_NOT_EXPIRE;
}
} }

View File

@@ -47,6 +47,7 @@ import org.springframework.beans.factory.BeanFactory;
public class ForEachFork extends JBPMSpringActionHandler public class ForEachFork extends JBPMSpringActionHandler
{ {
private static final long serialVersionUID = 4643103713602441652L; private static final long serialVersionUID = 4643103713602441652L;
private ServiceRegistry services; private ServiceRegistry services;
private Element foreach; private Element foreach;
@@ -190,7 +191,7 @@ public class ForEachFork extends JBPMSpringActionHandler
protected String getTokenName(Token parent, String transitionName, int loopIndex) protected String getTokenName(Token parent, String transitionName, int loopIndex)
{ {
String tokenName = null; String tokenName = null;
if (transitionName != null) if (transitionName != null && transitionName.length() > 0)
{ {
if (!parent.hasChild(transitionName)) if (!parent.hasChild(transitionName))
{ {
@@ -210,12 +211,13 @@ public class ForEachFork extends JBPMSpringActionHandler
else else
{ {
// no transition name // no transition name
int size = ( parent.getChildren()!=null ? parent.getChildren().size()+1 : 1 ); int size = (parent.getChildren() != null) ? parent.getChildren().size() + 1 : 1;
tokenName = Integer.toString(size); tokenName = "FOREACHFORK" + Integer.toString(size);
} }
return tokenName + "." + loopIndex; return tokenName + "." + loopIndex;
} }
/** /**
* Fork Transition * Fork Transition
*/ */

View File

@@ -84,6 +84,7 @@ import org.hibernate.Session;
import org.hibernate.criterion.Conjunction; import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction; import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Order; import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property; import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions; import org.hibernate.criterion.Restrictions;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
@@ -1302,7 +1303,6 @@ public class JBPMEngine extends BPMEngine
*/ */
private Criteria createTaskQueryCriteria(Session session, WorkflowTaskQuery query) private Criteria createTaskQueryCriteria(Session session, WorkflowTaskQuery query)
{ {
Criteria process = null;
Criteria task = session.createCriteria(TaskInstance.class); Criteria task = session.createCriteria(TaskInstance.class);
// task id // task id
@@ -1358,35 +1358,9 @@ public class JBPMEngine extends BPMEngine
} }
} }
// process active? // process criteria
if (query.isActive() != null) Criteria process = createProcessCriteria(task, query);
{
process = (process == null) ? task.createCriteria("processInstance") : process;
if (query.isActive())
{
process.add(Restrictions.isNull("end"));
}
else
{
process.add(Restrictions.isNotNull("end"));
}
}
// process id
if (query.getProcessId() != null)
{
process = (process == null) ? task.createCriteria("processInstance") : process;
process.add(Restrictions.eq("id", getJbpmId(query.getProcessId())));
}
// process name
if (query.getProcessName() != null)
{
process = (process == null) ? task.createCriteria("processInstance") : process;
Criteria processDef = process.createCriteria("processDefinition");
processDef.add(Restrictions.eq("name", query.getProcessName().toPrefixString(namespaceService)));
}
// process custom properties // process custom properties
if (query.getProcessCustomProps() != null) if (query.getProcessCustomProps() != null)
{ {
@@ -1398,7 +1372,7 @@ public class JBPMEngine extends BPMEngine
{ {
// create criteria for process variables // create criteria for process variables
Criteria variables = session.createCriteria(VariableInstance.class); Criteria variables = session.createCriteria(VariableInstance.class);
variables.setProjection(Property.forName("processInstance")); variables.setProjection(Projections.distinct(Property.forName("processInstance")));
Disjunction values = Restrictions.disjunction(); Disjunction values = Restrictions.disjunction();
for (Map.Entry<QName, Object> prop : props.entrySet()) for (Map.Entry<QName, Object> prop : props.entrySet())
{ {
@@ -1409,6 +1383,9 @@ public class JBPMEngine extends BPMEngine
} }
variables.add(values); variables.add(values);
// note: constrain process variables to same criteria as tasks
createProcessCriteria(variables, query);
// retrieve list of processes matching specified variables // retrieve list of processes matching specified variables
List<ProcessInstance> processList = variables.list(); List<ProcessInstance> processList = variables.list();
Object[] processIds = null; Object[] processIds = null;
@@ -1491,6 +1468,49 @@ public class JBPMEngine extends BPMEngine
return task; return task;
} }
/**
* Create process-specific query criteria
*
* @param root
* @param query
* @return
*/
private Criteria createProcessCriteria(Criteria root, WorkflowTaskQuery query)
{
Criteria process = null;
// process active?
if (query.isActive() != null)
{
process = (process == null) ? root.createCriteria("processInstance") : process;
if (query.isActive())
{
process.add(Restrictions.isNull("end"));
}
else
{
process.add(Restrictions.isNotNull("end"));
}
}
// process id
if (query.getProcessId() != null)
{
process = (process == null) ? root.createCriteria("processInstance") : process;
process.add(Restrictions.eq("id", getJbpmId(query.getProcessId())));
}
// process name
if (query.getProcessName() != null)
{
process = (process == null) ? root.createCriteria("processInstance") : process;
Criteria processDef = process.createCriteria("processDefinition");
processDef.add(Restrictions.eq("name", query.getProcessName().toPrefixString(namespaceService)));
}
return process;
}
/** /**
* Gets a jBPM Task Instance * Gets a jBPM Task Instance
* @param taskSession jBPM task session * @param taskSession jBPM task session