Moving to root below branch label

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2005-12-08 07:13:07 +00:00
commit e1e6508fec
1095 changed files with 230566 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Helper class to provide static and common access to the Spring
* {@link org.springframework.context.ApplicationContext application context}.
*
* @author Derek Hulley
*/
public class ApplicationContextHelper
{
/** location of required configuration files */
public static final String[] CONFIG_LOCATIONS = new String[] { "classpath:alfresco/application-context.xml" };
/**
* Instantiates a new application context.
*
* @return Returns a new application context
*/
public static ApplicationContext getApplicationContext()
{
return new ClassPathXmlApplicationContext(CONFIG_LOCATIONS);
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.transaction.TransactionService;
/**
* Base Alfresco test.
*
* Creates a store and root node that can be used in the tests.
*
* Runs all tests as the system user.
*
* @author Roy Wetherall
*/
public abstract class BaseAlfrescoSpringTest extends BaseSpringTest
{
/** The node service */
protected NodeService nodeService;
/** The content service */
protected ContentService contentService;
/** The authentication service */
protected AuthenticationService authenticationService;
/** The store reference */
protected StoreRef storeRef;
/** The root node reference */
protected NodeRef rootNodeRef;
protected ActionService actionService;
protected TransactionService transactionService;
/**
* On setup in transaction override
*/
@Override
protected void onSetUpInTransaction() throws Exception
{
super.onSetUpInTransaction();
// Get a reference to the node service
this.nodeService = (NodeService) this.applicationContext.getBean("nodeService");
this.contentService = (ContentService) this.applicationContext.getBean("contentService");
this.authenticationService = (AuthenticationService) this.applicationContext.getBean("authenticationService");
this.actionService = (ActionService)this.applicationContext.getBean("actionService");
this.transactionService = (TransactionService)this.applicationContext.getBean("transactionComponent");
// Authenticate as the system user
AuthenticationComponent authenticationComponent = (AuthenticationComponent) this.applicationContext
.getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
// Create the store and get the root node
this.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
this.rootNodeRef = this.nodeService.getRootNode(this.storeRef);
}
@Override
protected void onTearDownInTransaction()
{
authenticationService.clearCurrentSecurityContext();
super.onTearDownInTransaction();
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import junit.framework.TestCase;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.transaction.TransactionService;
import org.springframework.context.ApplicationContext;
/**
* Base Alfresco test.
*
* Creates a store and root node that can be used in the tests.
*
* @author Roy Wetherall
*/
public abstract class BaseAlfrescoTestCase extends TestCase
{
/** the context to keep between tests */
public static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
/** the service registry */
protected ServiceRegistry serviceRegistry;
/** The node service */
protected NodeService nodeService;
/** The content service */
protected ContentService contentService;
/** The authentication service */
protected AuthenticationService authenticationService;
/** The store reference */
protected StoreRef storeRef;
/** The root node reference */
protected NodeRef rootNodeRef;
protected ActionService actionService;
protected TransactionService transactionService;
@Override
protected void setUp() throws Exception
{
super.setUp();
// get the service register
this.serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
//Get a reference to the node service
this.nodeService = (NodeService)ctx.getBean("NodeService");
this.contentService = (ContentService)ctx.getBean("ContentService");
this.authenticationService = (AuthenticationService)ctx.getBean("authenticationService");
this.actionService = (ActionService)ctx.getBean("actionService");
this.transactionService = (TransactionService)ctx.getBean("transactionComponent");
// Authenticate as the system user - this must be done before we create the store
AuthenticationComponent authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
// Create the store and get the root node
this.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
this.rootNodeRef = this.nodeService.getRootNode(this.storeRef);
}
@Override
protected void tearDown() throws Exception
{
authenticationService.clearCurrentSecurityContext();
super.tearDown();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
/**
* Base test class providing Hibernate sessions.
* <p>
* By default this is auto-wired by type. If a this is going to
* result in a conlict the use auto-wire by name. This can be done by
* setting populateProtectedVariables to true in the constructor and
* then adding protected members with the same name as the bean you require.
*
* @author Derek Hulley
*/
public abstract class BaseSpringTest extends AbstractTransactionalDataSourceSpringContextTests
{
/** protected so that it gets populated if autowiring is done by variable name **/
protected SessionFactory sessionFactory;
/**
* Constructor
*/
public BaseSpringTest()
{
}
/**
* Setter present for in case autowiring is done by type
*
* @param sessionFactory
*/
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
/**
* @return Returns the existing session attached to the thread.
* A new session will <b>not</b> be created.
*/
protected Session getSession()
{
return SessionFactoryUtils.getSession(sessionFactory, true);
}
/**
* Forces the session to flush to the database (without commiting) and clear the
* cache. This ensures that all reads against the session are fresh instances,
* which gives the assurance that the DB read/write operations occur correctly.
*/
protected void flushAndClear()
{
getSession().flush();
getSession().clear();
}
/**
* Get the config locations
*
* @return an array containing the config locations
*/
protected String[] getConfigLocations()
{
if (logger.isDebugEnabled())
{
logger.debug("Getting config locations");
}
return ApplicationContextHelper.CONFIG_LOCATIONS;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import java.io.Serializable;
import java.util.HashMap;
import org.alfresco.service.namespace.QName;
/**
* Property map helper class.
* <p>
* This class can be used as a short hand when a class of type
* Map<QName, Serializable> is required.
*
* @author Roy Wetherall
*/
public class PropertyMap extends HashMap<QName, Serializable>
{
private static final long serialVersionUID = 8052326301073209645L;
/**
* @see HashMap#HashMap(int, float)
*/
public PropertyMap(int initialCapacity, float loadFactor)
{
super(initialCapacity, loadFactor);
}
/**
* @see HashMap#HashMap(int)
*/
public PropertyMap(int initialCapacity)
{
super(initialCapacity);
}
/**
* @see HashMap#HashMap()
*/
public PropertyMap()
{
super();
}
}

View File

@@ -0,0 +1,238 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import org.alfresco.repo.search.impl.lucene.QueryParser;
/**
* Helper class to provide conversions between different search languages
* @author Derek Hulley
*/
public class SearchLanguageConversion
{
/**
* XPath like query language summary:
* <ul>
* <li>Escape: \</li>
* <li>Single char search: _</li>
* <li>Multiple char search: %</li>
* <li>Reserved: \%_</li>
* </ul>
*/
public static LanguageDefinition DEF_XPATH_LIKE = new SimpleLanguageDef('\\', "%", "_", "\\%_");
/**
* Regular expression query language summary:
* <ul>
* <li>Escape: \</li>
* <li>Single char search: .</li>
* <li>Multiple char search: .*</li>
* <li>Reserved: \*.+?^$(){}|</li>
* </ul>
*/
public static LanguageDefinition DEF_REGEX = new SimpleLanguageDef('\\', ".*", ".", "\\*.+?^$(){}|");
/**
* Lucene syntax summary: {@link QueryParser#escape(String) Lucene Query Parser}
*/
public static LanguageDefinition DEF_LUCENE = new LuceneLanguageDef();
/**
* Escape a string according to the <b>XPath</b> like function syntax.
*
* @param str the string to escape
* @return Returns the escaped string
*/
public static String escapeForXPathLike(String str)
{
return escape(DEF_XPATH_LIKE, str);
}
/**
* Escape a string according to the <b>regex</b> language syntax.
*
* @param str the string to escape
* @return Returns the escaped string
*/
public static String escapeForRegex(String str)
{
return escape(DEF_REGEX, str);
}
/**
* Escape a string according to the <b>Lucene</b> query syntax.
*
* @param str the string to escape
* @return Returns the escaped string
*/
public static String escapeForLucene(String str)
{
return escape(DEF_LUCENE, str);
}
/**
* Generic escaping using the language definition
*/
private static String escape(LanguageDefinition def, String str)
{
StringBuilder sb = new StringBuilder(str.length() * 2);
char[] chars = str.toCharArray();
for (int i = 0; i < chars.length; i++)
{
// first check for reserved chars
if (def.isReserved(chars[i]))
{
// escape it
sb.append(def.escapeChar);
}
sb.append(chars[i]);
}
return sb.toString();
}
/**
* Convert an <b>xpath</b> like function clause into a <b>regex</b> query.
*
* @param xpathLikeClause
* @return Returns a valid regular expression that is equivalent to the
* given <b>xpath</b> like clause.
*/
public static String convertXPathLikeToRegex(String xpathLikeClause)
{
return convert(DEF_XPATH_LIKE, DEF_REGEX, xpathLikeClause);
}
/**
* Convert an <b>xpath</b> like function clause into a <b>Lucene</b> query.
*
* @param xpathLikeClause
* @return Returns a valid <b>Lucene</b> expression that is equivalent to the
* given <b>xpath</b> like clause.
*/
public static String convertXPathLikeToLucene(String xpathLikeClause)
{
return convert(DEF_XPATH_LIKE, DEF_LUCENE, xpathLikeClause);
}
public static String convert(LanguageDefinition from, LanguageDefinition to, String query)
{
char[] chars = query.toCharArray();
StringBuilder sb = new StringBuilder(chars.length * 2);
boolean escaping = false;
for (int i = 0; i < chars.length; i++)
{
if (escaping) // if we are currently escaping, just escape the current character
{
sb.append(to.escapeChar); // the to format escape char
sb.append(chars[i]); // the current char
escaping = false;
}
else if (chars[i] == from.escapeChar) // not escaping and have escape char
{
escaping = true;
}
else if (query.startsWith(from.multiCharWildcard, i)) // not escaping but have multi-char wildcard
{
// translate the wildcard
sb.append(to.multiCharWildcard);
}
else if (query.startsWith(from.singleCharWildcard, i)) // have single-char wildcard
{
// translate the wildcard
sb.append(to.singleCharWildcard);
}
else if (to.isReserved(chars[i])) // reserved character
{
sb.append(to.escapeChar).append(chars[i]);
}
else // just a normal char in both
{
sb.append(chars[i]);
}
}
return sb.toString();
}
/**
* Simple store of special characters for a given query language
*/
public static abstract class LanguageDefinition
{
public final char escapeChar;
public final String multiCharWildcard;
public final String singleCharWildcard;
public LanguageDefinition(char escapeChar, String multiCharWildcard, String singleCharWildcard)
{
this.escapeChar = escapeChar;
this.multiCharWildcard = multiCharWildcard;
this.singleCharWildcard = singleCharWildcard;
}
public abstract boolean isReserved(char ch);
}
private static class SimpleLanguageDef extends LanguageDefinition
{
private String reserved;
public SimpleLanguageDef(char escapeChar, String multiCharWildcard, String singleCharWildcard, String reserved)
{
super(escapeChar, multiCharWildcard, singleCharWildcard);
this.reserved = reserved;
}
@Override
public boolean isReserved(char ch)
{
return (reserved.indexOf(ch) > -1);
}
}
private static class LuceneLanguageDef extends LanguageDefinition
{
private String reserved;
public LuceneLanguageDef()
{
super('\\', "*", "?");
init();
}
/**
* Discovers all the reserved chars
*/
private void init()
{
StringBuilder sb = new StringBuilder(20);
for (char ch = 0; ch < 256; ch++)
{
char[] chars = new char[] {ch};
String unescaped = new String(chars);
// check it
String escaped = QueryParser.escape(unescaped);
if (!escaped.equals(unescaped))
{
// it was escaped
sb.append(ch);
}
}
reserved = sb.toString();
}
@Override
public boolean isReserved(char ch)
{
return (reserved.indexOf(ch) > -1);
}
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import junit.framework.TestCase;
/**
* @see org.alfresco.util.SearchLanguageConversion
*
* @author Derek Hulley
*/
public class SearchLanguageConversionTest extends TestCase
{
/**
* A string with a whole lod of badness to stress test with
*/
private static final String BAD_STRING =
"\\ | ! \" <20> " +
"$ % ^ & * ( " +
") _ { } [ ] " +
"@ # ~ ' : ; " +
", . < > + ? " +
"/ \\\\ \\* \\? \\_";
public void testEscapeXPathLike()
{
String good = SearchLanguageConversion.escapeForXPathLike(BAD_STRING);
assertEquals("Escaping for xpath failed",
"\\\\ | ! \" <20> " +
"$ \\% ^ & * ( " +
") \\_ { } [ ] " +
"@ # ~ ' : ; " +
", . < > + ? " +
"/ \\\\\\\\ \\\\* \\\\? \\\\\\_",
good);
}
public void testEscapeRegex()
{
String good = SearchLanguageConversion.escapeForRegex(BAD_STRING);
assertEquals("Escaping for regex failed",
"\\\\ \\| ! \" <20> " +
"\\$ % \\^ & \\* \\( " +
"\\) _ \\{ \\} [ ] " +
"@ # ~ ' : ; " +
", \\. < > \\+ \\? " +
"/ \\\\\\\\ \\\\\\* \\\\\\? \\\\_",
good);
}
public void testEscapeLucene()
{
String good = SearchLanguageConversion.escapeForLucene(BAD_STRING);
assertEquals("Escaping for regex failed",
"\\\\ | \\! \\\" <20> " +
"$ % \\^ & \\* \\( " +
"\\) _ \\{ \\} \\[ \\] " +
"@ # \\~ ' \\: ; " +
", . < > \\+ \\? " +
"/ \\\\\\\\ \\\\\\* \\\\\\? \\\\_",
good);
}
public void testConvertXPathLikeToRegex()
{
String good = SearchLanguageConversion.convertXPathLikeToRegex(BAD_STRING);
assertEquals("XPath like to regex failed",
"\\ \\| ! \" <20> " +
"\\$ .* \\^ & \\* \\( " +
"\\) . \\{ \\} [ ] " +
"@ # ~ ' : ; " +
", \\. < > \\+ \\? " +
"/ \\\\ \\* \\? \\_",
good);
}
public void testConvertXPathLikeToLucene()
{
String good = SearchLanguageConversion.convertXPathLikeToLucene(BAD_STRING);
assertEquals("XPath like to regex failed",
"\\ | \\! \\\" <20> " +
"$ * \\^ & \\* \\( " +
"\\) ? \\{ \\} \\[ \\] " +
"@ # \\~ ' \\: ; " +
", . < > \\+ \\? " +
"/ \\\\ \\* \\? \\_",
good);
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import java.io.Serializable;
import java.util.HashMap;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* Utility class containing some useful methods to help when writing tets that require authenticated users
*
* @author Roy Wetherall
*/
public abstract class TestWithUserUtils extends BaseSpringTest
{
/**
* Create a new user, including the corresponding person node.
*
* @param userName the user name
* @param password the password
* @param rootNodeRef the root node reference
* @param nodeService the node service
* @param authenticationService the authentication service
*/
public static void createUser(
String userName,
String password,
NodeRef rootNodeRef,
NodeService nodeService,
AuthenticationService authenticationService)
{
QName children = ContentModel.ASSOC_CHILDREN;
QName system = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "system");
QName container = ContentModel.TYPE_CONTAINER;
QName types = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "people");
NodeRef systemNodeRef = nodeService.createNode(rootNodeRef, children, system, container).getChildRef();
NodeRef typesNodeRef = nodeService.createNode(systemNodeRef, children, types, container).getChildRef();
HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>();
properties.put(ContentModel.PROP_USERNAME, userName);
NodeRef goodUserPerson = nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, properties).getChildRef();
// Create the users
authenticationService.createAuthentication(userName, password.toCharArray());
}
/**
* Autneticate the user with the specified password
*
* @param userName the user name
* @param password the password
* @param rootNodeRef the root node reference
* @param authenticationService the authentication service
*/
public static void authenticateUser(
String userName,
String password,
NodeRef rootNodeRef,
AuthenticationService authenticationService)
{
authenticationService.authenticate(userName, password.toCharArray());
}
/**
* Get the current user node reference
*
* @param authenticationService the authentication service
* @return the currenlty authenticated user's node reference
*/
public static String getCurrentUser(AuthenticationService authenticationService)
{
String un = authenticationService.getCurrentUserName();
if (un != null)
{
return un;
}
else
{
throw new RuntimeException("The current user could not be retrieved.");
}
}
public static void deleteUser(String user_name, String pwd, NodeRef ref, NodeService service, AuthenticationService service2)
{
service2.deleteAuthentication(user_name);
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.alfresco.error.AlfrescoRuntimeException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Factory for {@link java.util.concurrent.ThreadPoolExecutor} instances,
* which cannot easily be constructed using constructor injection.
* <p>
* This factory provides the a singleton instance of the pool.
*
* @author Derek Hulley
*/
public class ThreadPoolExecutorFactoryBean implements FactoryBean, InitializingBean
{
private int corePoolSize;
private int maximumPoolSize;
private int keepAliveTime;
private BlockingQueue<Runnable> workQueue;
private ThreadPoolExecutor instance;
/**
* Constructor setting default properties:
* <ul>
* <li>corePoolSize: 5</li>
* <li>maximumPoolSize: 20</li>
* <li>keepAliveTime: 60s</li>
* <li>workQueue: {@link ArrayBlockingQueue}</li>
* </ul>
*/
public ThreadPoolExecutorFactoryBean()
{
corePoolSize = 5;
maximumPoolSize = 20;
keepAliveTime = 30;
}
/**
* The number of threads to keep in the pool, even if idle.
*
* @param corePoolSize core thread count
*/
public void setCorePoolSize(int corePoolSize)
{
this.corePoolSize = corePoolSize;
}
/**
* The maximum number of threads to keep in the pool
*
* @param maximumPoolSize the maximum number of threads in the pool
*/
public void setMaximumPoolSize(int maximumPoolSize)
{
this.maximumPoolSize = maximumPoolSize;
}
/**
* The time (in seconds) to keep non-core idle threads in the pool
*
* @param keepAliveTime time to stay idle in seconds
*/
public void setKeepAliveTime(int keepAliveTime)
{
this.keepAliveTime = keepAliveTime;
}
/**
* The optional queue instance to use
*
* @param workQueue optional queue implementation
*/
public void setWorkQueue(BlockingQueue<Runnable> workQueue)
{
this.workQueue = workQueue;
}
public void afterPropertiesSet() throws Exception
{
if (workQueue == null)
{
workQueue = new ArrayBlockingQueue<Runnable>(corePoolSize);
}
// construct the instance
instance = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue);
}
/**
* @return Returns true always.
*/
public boolean isSingleton()
{
return true;
}
/**
* @return Returns the singleton {@link ThreadPoolExecutor instance}.
*/
public Object getObject() throws Exception
{
if (instance == null)
{
throw new AlfrescoRuntimeException("The ThreadPoolExecutor instance has not been created");
}
return instance;
}
/**
* @see ThreadPoolExecutor
*/
public Class getObjectType()
{
return ThreadPoolExecutor.class;
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util.debug;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Performs writing to DEBUG of incoming arguments and outgoing results for a method call.<br>
* If the method invocation throws an exception, then the incoming arguments are
* logged to DEBUG as well.<br>
* The implementation adds very little overhead to a normal method
* call by only building log messages when required.
* <p>
* The logging is done against the logger retrieved using the names:
* <p>
* <pre>
* org.alfresco.util.debug.MethodCallLogAdvice
* AND
* targetClassName
* targetClassName.methodName
* targetClassName.methodName.exception
* </pre>
* <p>
* The following examples show how to control the log levels:
* <p>
* <pre>
* org.alfresco.util.debug.MethodCallLogAdvice=DEBUG # activate method logging
* AND
* x.y.MyClass=DEBUG # log debug for all method calls on MyClass
* x.y.MyClass.doSomething=DEBUG # log debug for all doSomething method calls
* x.y.MyClass.doSomething.exception=DEBUG # only log debug for doSomething() upon exception
* </pre>
* <p>
*
* @author Derek Hulley
*/
public class MethodCallLogAdvice implements MethodInterceptor
{
private static final Log logger = LogFactory.getLog(MethodCallLogAdvice.class);
public Object invoke(MethodInvocation invocation) throws Throwable
{
if (logger.isDebugEnabled())
{
return invokeWithLogging(invocation);
}
else
{
// no logging required
return invocation.proceed();
}
}
/**
* Only executes logging code if logging is required
*/
private Object invokeWithLogging(MethodInvocation invocation) throws Throwable
{
String methodName = invocation.getMethod().getName();
String className = invocation.getMethod().getDeclaringClass().getName();
// execute as normal
try
{
Object ret = invocation.proceed();
// logging
Log methodLogger = LogFactory.getLog(className + "." + methodName);
if (methodLogger.isDebugEnabled())
{
// log success
StringBuffer sb = getInvocationInfo(className, methodName, invocation.getArguments());
sb.append(" Result: ").append(ret);
methodLogger.debug(sb);
}
// done
return ret;
}
catch (Throwable e)
{
Log exceptionLogger = LogFactory.getLog(className + "." + methodName + ".exception");
if (exceptionLogger.isDebugEnabled())
{
StringBuffer sb = getInvocationInfo(className, methodName, invocation.getArguments());
sb.append(" Failure: ").append(e.getClass().getName()).append(" - ").append(e.getMessage());
exceptionLogger.debug(sb);
}
// rethrow
throw e;
}
}
/**
* Return format:
* <pre>
* Method: className#methodName
* Argument: arg0
* Argument: arg1
* ...
* Argument: argN {newline}
* </pre>
*
* @param className
* @param methodName
* @param args
* @return Returns a StringBuffer containing the details of a method call
*/
private StringBuffer getInvocationInfo(String className, String methodName, Object[] args)
{
StringBuffer sb = new StringBuffer(250);
sb.append("\nMethod: ").append(className).append("#").append(methodName).append("\n");
sb.append(" Transaction: ").append(AlfrescoTransactionSupport.getTransactionId()).append("\n");
for (Object arg : args)
{
sb.append(" Argument: ").append(arg).append("\n");
}
return sb;
}
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util.debug;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
/**
* Debug class that has methods to inspect the contents of a node store.
*
* @author Roy Wetherall
*/
public class NodeStoreInspector
{
/**
* Dumps the contents of a store to a string.
*
* @param nodeService the node service
* @param storeRef the store reference
* @return string containing textual representation of the contents of the store
*/
public static String dumpNodeStore(NodeService nodeService, StoreRef storeRef)
{
StringBuilder builder = new StringBuilder();
if (nodeService.exists(storeRef) == true)
{
NodeRef rootNode = nodeService.getRootNode(storeRef);
builder.append(outputNode(0, nodeService, rootNode));
}
else
{
builder.
append("The store ").
append(storeRef.toString()).
append(" does not exist.");
}
return builder.toString();
}
/**
* Output the node
*
* @param iIndent
* @param nodeService
* @param nodeRef
* @return
*/
private static String outputNode(int iIndent, NodeService nodeService, NodeRef nodeRef)
{
StringBuilder builder = new StringBuilder();
try
{
QName nodeType = nodeService.getType(nodeRef);
builder.
append(getIndent(iIndent)).
append("node: ").
append(nodeRef.getId()).
append(" (").
append(nodeType.getLocalName());
Collection<QName> aspects = nodeService.getAspects(nodeRef);
for (QName aspect : aspects)
{
builder.
append(", ").
append(aspect.getLocalName());
}
builder.append(")\n");
Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
for (QName name : props.keySet())
{
String valueAsString = "null";
Serializable value = props.get(name);
if (value != null)
{
valueAsString = value.toString();
}
builder.
append(getIndent(iIndent+1)).
append("@").
append(name.getLocalName()).
append(" = ").
append(valueAsString).
append("\n");
}
Collection<ChildAssociationRef> childAssocRefs = nodeService.getChildAssocs(nodeRef);
for (ChildAssociationRef childAssocRef : childAssocRefs)
{
builder.
append(getIndent(iIndent+1)).
append("-> ").
append(childAssocRef.getQName().toString()).
append(" (").
append(childAssocRef.getQName().toString()).
append(")\n");
builder.append(outputNode(iIndent+2, nodeService, childAssocRef.getChildRef()));
}
Collection<AssociationRef> assocRefs = nodeService.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL);
for (AssociationRef assocRef : assocRefs)
{
builder.
append(getIndent(iIndent+1)).
append("-> associated to ").
append(assocRef.getTargetRef().getId()).
append("\n");
}
}
catch (InvalidNodeRefException invalidNode)
{
invalidNode.printStackTrace();
}
return builder.toString();
}
/**
* Get the indent
*
* @param iIndent the indent value
* @return the indent string
*/
private static String getIndent(int iIndent)
{
StringBuilder builder = new StringBuilder(iIndent*3);
for (int i = 0; i < iIndent; i++)
{
builder.append(" ");
}
return builder.toString();
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util.debug;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.BaseSpringTest;
/**
* @author Roy Wetherall
*/
public class OutputSpacesStoreSystemTest extends BaseSpringTest
{
/**
* Dump the contents of the spaces store to standard out
*/
public void testDumpSpacesStore()
{
NodeService nodeService = (NodeService)this.applicationContext.getBean("nodeService");
StoreRef spacesStore = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
System.out.println(NodeStoreInspector.dumpNodeStore(nodeService, spacesStore));
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util.perf;
import java.text.DecimalFormat;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* An instance of this class keeps track of timings of method calls made against
* a named entity. Logging can occur either after each recorded time, or only on
* VM shutdown or both.
* <p>
* Logging output is managed down to either the entity or entity-invocation level as follows:
* <p>
* <pre>
* performance.summary.method
* performance.summary.vm
* AND
* performance.targetEntityName
* performance.targetEntityName.methodName
* </pre>
* <p>
* The following examples illustrate how it can be used:
* <p>
* <pre>
* performance.summary.method=DEBUG
* performance.myBean=DEBUG
* --> Output method invocation statistic on each call to myBean
*
* performance.summary.vm=DEBUG
* performance.myBean.doSomething=DEBUG
* --> Output summary for doSomething() invocations on myBean when VM terminates
*
* performance=DEBUG
* --> Output all performance data - after each invocation and upon VM closure
* </pre>
* <p>
*
* @author Derek Hulley
*/
public abstract class AbstractPerformanceMonitor
{
/** logger for method level performance summaries */
private static final Log methodSummaryLogger = LogFactory.getLog("performance.summary.method");
/** logger for VM level performance summaries */
private static final Log vmSummaryLogger = LogFactory.getLog("performance.summary.vm");
private final String entityName;
/** VM level summary */
private SortedMap<String, MethodStats> stats;
/**
* Convenience method to check if there is some sort of performance logging enabled
*
* @return Returns true if there is some sort of performance logging enabled, false otherwise
*/
public static boolean isDebugEnabled()
{
return (vmSummaryLogger.isDebugEnabled() || methodSummaryLogger.isDebugEnabled());
}
/**
* @param entityName the name of the entity for which the performance is being recorded
*/
public AbstractPerformanceMonitor(String entityName)
{
this.entityName = entityName;
stats = new TreeMap<String, MethodStats>();
// enable a VM shutdown hook if required
if (vmSummaryLogger.isDebugEnabled())
{
Thread hook = new ShutdownThread();
Runtime.getRuntime().addShutdownHook(hook);
}
}
/**
* Dumps the results of the method execution to:
* <ul>
* <li>DEBUG output if the method level debug logging is active</li>
* <li>Performance store if required</li>
* </ul>
*
* @param methodName the name of the method against which to store the results
* @param delayMs
*/
protected void recordStats(String methodName, double delayMs)
{
Log methodLogger = LogFactory.getLog("performance." + entityName + "." + methodName);
if (!methodLogger.isDebugEnabled())
{
// no recording for this method
return;
}
DecimalFormat format = new DecimalFormat ();
format.setMinimumFractionDigits (3);
format.setMaximumFractionDigits (3);
// must we log on a per-method call?
if (methodSummaryLogger.isDebugEnabled())
{
methodLogger.debug("Executed " + entityName + "#" + methodName + " in " + format.format(delayMs) + "ms");
}
if (vmSummaryLogger.isDebugEnabled())
{
synchronized(this) // only synchronize if absolutely necessary
{
// get stats
MethodStats methodStats = stats.get(methodName);
if (methodStats == null)
{
methodStats = new MethodStats();
stats.put(methodName, methodStats);
}
methodStats.record(delayMs);
}
}
}
/**
* Stores the execution count and total execution time for any method
*/
private class MethodStats
{
private int count;
private double totalTimeMs;
/**
* Records the time for a method to execute and bumps up the execution count
*
* @param delayMs the time the method took to execute in milliseconds
*/
public void record(double delayMs)
{
count++;
totalTimeMs += delayMs;
}
public String toString()
{
DecimalFormat format = new DecimalFormat ();
format.setMinimumFractionDigits (3);
format.setMaximumFractionDigits (3);
double averageMs = totalTimeMs / (long) count;
return ("Executed " + count + " times, averaging " + format.format(averageMs) + "ms per call");
}
}
/**
* Dumps the output of all recorded method statistics
*/
private class ShutdownThread extends Thread
{
public void run()
{
String beanName = AbstractPerformanceMonitor.this.entityName;
// prevent multiple ShutdownThread instances interleaving their output
synchronized(ShutdownThread.class)
{
vmSummaryLogger.debug("\n==================== " + beanName.toUpperCase() + " ===================");
Set<String> methodNames = stats.keySet();
for (String methodName : methodNames)
{
vmSummaryLogger.debug("\nMethod performance summary: \n" +
" Bean: " + AbstractPerformanceMonitor.this.entityName + "\n" +
" Method: " + methodName + "\n" +
" Statistics: " + stats.get(methodName));
}
}
}
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util.perf;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.vladium.utils.timing.ITimer;
import com.vladium.utils.timing.TimerFactory;
/**
* Enables <b>begin ... end</b> style performance monitoring with summarisation
* using the <b>performance</b> logging category. It is designed to only incur
* a minor cost when performance logging is turned on using the DEBUG logging
* mechanism. See base class for details on enabling the <b>performance</b>
* logging categories.
* <p>
* This class is thread safe.
* <p>
* Usage:
* <pre>
* private PerformanceMonitor somethingTimer = new PerformanceMonitor("mytest", "doSomething");
* ...
* ...
* private void doSomething()
* {
* somethingTimer.start();
* ...
* ...
* somethingTimer.stop();
* }
* </pre>
*
* @author Derek Hulley
*/
public class PerformanceMonitor extends AbstractPerformanceMonitor
{
private String methodName;
private ThreadLocal<ITimer> threadLocalTimer;
private boolean log;
/**
* @param entityName name of the entity, e.g. a test name or a bean name against which to
* log the performance
* @param methodName the method for which the performance will be logged
*/
public PerformanceMonitor(String entityName, String methodName)
{
super(entityName);
this.methodName = methodName;
this.threadLocalTimer = new ThreadLocal<ITimer>();
// check if logging can be eliminated
Log methodLogger = LogFactory.getLog("performance." + entityName + "." + methodName);
this.log = AbstractPerformanceMonitor.isDebugEnabled() && methodLogger.isDebugEnabled();
}
/**
* Threadsafe method to start the timer.
* <p>
* The timer is only started if the logging levels are enabled.
*
* @see #stop()
*/
public void start()
{
if (!log)
{
// don't bother timing
return;
}
// overwrite the thread's timer
ITimer timer = TimerFactory.newTimer();
threadLocalTimer.set(timer);
// start the timer
timer.start();
}
/**
* Threadsafe method to stop the timer.
*
* @see #start()
*/
public void stop()
{
if (!log)
{
// don't bother timing
return;
}
// get the thread's timer
ITimer timer = threadLocalTimer.get();
if (timer == null)
{
// begin not called on the thread
return;
}
// time it
timer.stop();
recordStats(methodName, timer.getDuration());
// drop the thread's timer
threadLocalTimer.set(null);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util.perf;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import com.vladium.utils.timing.ITimer;
import com.vladium.utils.timing.TimerFactory;
/**
* An instance of this class keeps track of timings of method calls on a bean
*
* @author Derek Hulley
*/
public class PerformanceMonitorAdvice extends AbstractPerformanceMonitor implements MethodInterceptor
{
public PerformanceMonitorAdvice(String beanName)
{
super(beanName);
}
public Object invoke(MethodInvocation invocation) throws Throwable
{
// bypass all recording if performance logging is not required
if (AbstractPerformanceMonitor.isDebugEnabled())
{
return invokeWithLogging(invocation);
}
else
{
// no logging required
return invocation.proceed();
}
}
private Object invokeWithLogging(MethodInvocation invocation) throws Throwable
{
// get the time prior to call
ITimer timer = TimerFactory.newTimer ();
timer.start ();
//long start = System.currentTimeMillis();
// execute - do not record exceptions
Object ret = invocation.proceed();
// get time after call
//long end = System.currentTimeMillis();
// record the stats
timer.stop ();
recordStats(invocation.getMethod().getName(), timer.getDuration ());
// done
return ret;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.util.perf;
import java.lang.reflect.Method;
import junit.framework.TestCase;
/**
* Enabled vm performance monitoring for <b>performance.summary.vm</b> and
* <b>performance.PerformanceMonitorTest</b> to check.
*
* @see org.alfresco.util.perf.PerformanceMonitor
*
* @author Derek Hulley
*/
public class PerformanceMonitorTest extends TestCase
{
private PerformanceMonitor testTimingMonitor;
@Override
public void setUp() throws Exception
{
Method testTimingMethod = PerformanceMonitorTest.class.getMethod("testTiming");
testTimingMonitor = new PerformanceMonitor("PerformanceMonitorTest", "testTiming");
}
public void testSetUp() throws Exception
{
assertNotNull(testTimingMonitor);
}
public synchronized void testTiming() throws Exception
{
testTimingMonitor.start();
wait(50);
testTimingMonitor.stop();
}
}