mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-16 17:55:15 +00:00
Merged V2.2 to HEAD
8121: Merged V2.1 to V2.2 8088: Turned off debug logging. 8090: Tweaked session cache limiting for AVM. 8095: Fix for issue raised in ACT 402 8108: Fix for AWC-1816 8115: Build fix 8117: Fix AR-1217: OpenOffice connection is actively maintained git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8480 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
parent
316924e8de
commit
1f8c86d46d
@ -13,7 +13,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="interceptorNames">
|
<property name="interceptorNames">
|
||||||
<list>
|
<list>
|
||||||
<value>sessionSizeResourceInterceptor</value>
|
<value>avmSessionSizeResourceInterceptor</value>
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
@ -378,4 +378,26 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="avmSessionSizeResourceInterceptor" class="org.alfresco.repo.transaction.TransactionResourceInterceptor" >
|
||||||
|
<property name="methodResourceManagers">
|
||||||
|
<list>
|
||||||
|
<ref bean="avmSessionSizeResourceManager"></ref>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="elapsedTimeBeforeActivationMillis">
|
||||||
|
<value>500</value>
|
||||||
|
</property>
|
||||||
|
<property name="resourceManagerCallFrequencyMillis">
|
||||||
|
<value>250</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="avmSessionSizeResourceManager" class="org.alfresco.repo.domain.hibernate.SessionSizeResourceManager">
|
||||||
|
<property name="sessionFactory">
|
||||||
|
<ref bean="sessionFactory" />
|
||||||
|
</property>
|
||||||
|
<property name="threshold">
|
||||||
|
<value>100</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
</beans>
|
</beans>
|
||||||
|
@ -3,12 +3,8 @@
|
|||||||
|
|
||||||
<beans>
|
<beans>
|
||||||
|
|
||||||
<!-- Forces the reindexing of nodes where content may have been missing before -->
|
<!-- Forces the reindexing of content where full text extraction has previously failed -->
|
||||||
<!--
|
<bean id="missingFullTextReindexTrigger" class="org.alfresco.util.CronTriggerBean">
|
||||||
This component can be triggered at intervals where asynchronous content sharing
|
|
||||||
between clustered servers has been set up.
|
|
||||||
-->
|
|
||||||
<bean id="missingContentReindexTrigger" class="org.alfresco.util.TriggerBean">
|
|
||||||
<property name="jobDetail">
|
<property name="jobDetail">
|
||||||
<bean class="org.springframework.scheduling.quartz.JobDetailBean">
|
<bean class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||||
<property name="jobClass">
|
<property name="jobClass">
|
||||||
@ -17,7 +13,7 @@
|
|||||||
<property name="jobDataAsMap">
|
<property name="jobDataAsMap">
|
||||||
<map>
|
<map>
|
||||||
<entry key="indexRecoveryComponent">
|
<entry key="indexRecoveryComponent">
|
||||||
<ref bean="missingContentReindexComponent" />
|
<ref bean="missingFullTextReindexComponent" />
|
||||||
</entry>
|
</entry>
|
||||||
</map>
|
</map>
|
||||||
</property>
|
</property>
|
||||||
@ -26,12 +22,8 @@
|
|||||||
<property name="scheduler">
|
<property name="scheduler">
|
||||||
<ref bean="schedulerFactory" />
|
<ref bean="schedulerFactory" />
|
||||||
</property>
|
</property>
|
||||||
<!-- Give the server 5 minutes and then check for missing content -->
|
<property name="cronExpression">
|
||||||
<property name="startDelayMinutes">
|
<value>0 0 21 * * ?</value>
|
||||||
<value>5</value>
|
|
||||||
</property>
|
|
||||||
<property name="repeatCount">
|
|
||||||
<value>0</value>
|
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
@ -143,10 +143,16 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- Missing Content -->
|
<!-- Failed FTS reindexer -->
|
||||||
<!-- Bean that attempts to index content that was previously missing -->
|
<!--
|
||||||
|
Bean that triggers full text indexing for content that was previously flagged with:
|
||||||
|
nitf: Not Indexed Transformation Failed
|
||||||
|
nicm: Not Indexed Content Missing
|
||||||
|
nint: Not Indexed No Transformation
|
||||||
|
Use a CronTriggerBean and Quartz scheduler to trigger as required
|
||||||
|
-->
|
||||||
<bean
|
<bean
|
||||||
id="missingContentReindexComponent"
|
id="missingFullTextReindexComponent"
|
||||||
class="org.alfresco.repo.node.index.MissingContentReindexComponent"
|
class="org.alfresco.repo.node.index.MissingContentReindexComponent"
|
||||||
parent="indexRecoveryComponentBase">
|
parent="indexRecoveryComponentBase">
|
||||||
</bean>
|
</bean>
|
||||||
|
@ -16,4 +16,6 @@ system.config_check.warn.starting_with_errors=Alfresco is starting with errors.
|
|||||||
|
|
||||||
# OpenOffice
|
# OpenOffice
|
||||||
system.openoffice.info.connection_verified=The connection to OpenOffice has been established.
|
system.openoffice.info.connection_verified=The connection to OpenOffice has been established.
|
||||||
system.openoffice.err.connection_failed=A connection to OpenOffice could not be established.
|
system.openoffice.err.connection_failed=An initial OpenOffice connection could not be established.
|
||||||
|
system.openoffice.err.connection_lost=The OpenOffice connection has been lost.
|
||||||
|
system.openoffice.err.connection_remade=The OpenOffice connection was re-established.
|
@ -830,7 +830,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="interceptorNames">
|
<property name="interceptorNames">
|
||||||
<list>
|
<list>
|
||||||
<value>sessionSizeResourceInterceptor</value>
|
<value>avmSessionSizeResourceInterceptor</value>
|
||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
@ -56,6 +56,9 @@ system.bootstrap.config_check.strict=true
|
|||||||
# 0 prevents further logins, including the ability to enter single-user mode
|
# 0 prevents further logins, including the ability to enter single-user mode
|
||||||
server.maxusers=-1
|
server.maxusers=-1
|
||||||
|
|
||||||
|
# The Cron expression controlling the frequency with which the OpenOffice connection is tested
|
||||||
|
openOffice.test.cronExpression=0 * * * * ?
|
||||||
|
|
||||||
#
|
#
|
||||||
# Properties to limit resources spent on individual searches
|
# Properties to limit resources spent on individual searches
|
||||||
#
|
#
|
||||||
|
@ -133,6 +133,30 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="openOfficeConnectionTesterJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||||
|
<property name="jobClass">
|
||||||
|
<value>org.alfresco.util.OpenOfficeConnectionTester$OpenOfficeConnectionTesterJob</value>
|
||||||
|
</property>
|
||||||
|
<property name="jobDataAsMap">
|
||||||
|
<map>
|
||||||
|
<entry key="openOfficeConnectionTester">
|
||||||
|
<ref bean="openOfficeConnectionTester" />
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
<bean id="openOfficeConnectionTesterTrigger" class="org.alfresco.util.CronTriggerBean">
|
||||||
|
<property name="jobDetail">
|
||||||
|
<ref bean="openOfficeConnectionTesterJobDetail" />
|
||||||
|
</property>
|
||||||
|
<property name="scheduler">
|
||||||
|
<ref bean="schedulerFactory" />
|
||||||
|
</property>
|
||||||
|
<property name="cronExpression">
|
||||||
|
<value>${openOffice.test.cronExpression}</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="indexBackupJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
<bean id="indexBackupJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||||
<property name="jobClass">
|
<property name="jobClass">
|
||||||
<value>org.alfresco.repo.search.impl.lucene.AbstractLuceneIndexerAndSearcherFactory$LuceneIndexBackupJob</value>
|
<value>org.alfresco.repo.search.impl.lucene.AbstractLuceneIndexerAndSearcherFactory$LuceneIndexBackupJob</value>
|
||||||
|
@ -245,10 +245,16 @@ class AVMNodeDAOHibernate extends HibernateDaoSupport implements
|
|||||||
*/
|
*/
|
||||||
public void clear()
|
public void clear()
|
||||||
{
|
{
|
||||||
fgLogger.error(getSession().getStatistics());
|
if (fgLogger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
fgLogger.debug(getSession().getStatistics());
|
||||||
|
}
|
||||||
getSession().flush();
|
getSession().flush();
|
||||||
getSession().clear();
|
getSession().clear();
|
||||||
fgLogger.error(getSession().getStatistics());
|
if (fgLogger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
fgLogger.debug(getSession().getStatistics());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
|
@ -67,6 +67,23 @@ public class MetadataExtracterRegistry
|
|||||||
extracterCacheReadLock = extractionCacheLock.readLock();
|
extracterCacheReadLock = extractionCacheLock.readLock();
|
||||||
extracterCacheWriteLock = extractionCacheLock.writeLock();
|
extracterCacheWriteLock = extractionCacheLock.writeLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the registry to drop its cache of extractors. This is useful for the case where an extractor
|
||||||
|
* becomes available only after the registry has initialized the cache.
|
||||||
|
*/
|
||||||
|
public void resetCache()
|
||||||
|
{
|
||||||
|
extracterCacheWriteLock.lock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
extracterCache.clear();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
extracterCacheWriteLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an instance of an extracter for use
|
* Register an instance of an extracter for use
|
||||||
|
@ -99,7 +99,10 @@ public class MissingContentReindexComponent extends AbstractReindexComponent
|
|||||||
|
|
||||||
// search for it in the index, sorting with youngest first
|
// search for it in the index, sorting with youngest first
|
||||||
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
|
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
|
||||||
sp.setQuery("TEXT:" + AbstractLuceneIndexerImpl.NOT_INDEXED_CONTENT_MISSING);
|
sp.setQuery(
|
||||||
|
"TEXT:" + AbstractLuceneIndexerImpl.NOT_INDEXED_CONTENT_MISSING +
|
||||||
|
" TEXT: " + AbstractLuceneIndexerImpl.NOT_INDEXED_TRANSFORMATION_FAILED +
|
||||||
|
" TEXT: " + AbstractLuceneIndexerImpl.NOT_INDEXED_NO_TRANSFORMATION);
|
||||||
sp.addSort(SearchParameters.SORT_IN_DOCUMENT_ORDER_DESCENDING);
|
sp.addSort(SearchParameters.SORT_IN_DOCUMENT_ORDER_DESCENDING);
|
||||||
ResultSet results = null;
|
ResultSet results = null;
|
||||||
try
|
try
|
||||||
@ -121,6 +124,7 @@ public class MissingContentReindexComponent extends AbstractReindexComponent
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
transactionService.getRetryingTransactionHelper().doInTransaction(reindexWork);
|
transactionService.getRetryingTransactionHelper().doInTransaction(reindexWork);
|
||||||
|
count++;
|
||||||
// check if we have to break out
|
// check if we have to break out
|
||||||
if (isShuttingDown())
|
if (isShuttingDown())
|
||||||
{
|
{
|
||||||
|
@ -96,7 +96,7 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC
|
|||||||
{
|
{
|
||||||
this.transactionService = transactionService;
|
this.transactionService = transactionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransactionService getTransactionService()
|
public TransactionService getTransactionService()
|
||||||
{
|
{
|
||||||
return transactionService;
|
return transactionService;
|
||||||
@ -141,13 +141,20 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC
|
|||||||
|
|
||||||
public Authentication execute() throws Throwable
|
public Authentication execute() throws Throwable
|
||||||
{
|
{
|
||||||
NodeRef userNode = personService.getPerson(userName);
|
if (personService.personExists(userName))
|
||||||
if (userNode != null)
|
|
||||||
{
|
{
|
||||||
// Get the person name and use that as the current user to line up with permission checks
|
NodeRef userNode = personService.getPerson(userName);
|
||||||
String personName = (String) nodeService.getProperty(userNode, ContentModel.PROP_USERNAME);
|
if (userNode != null)
|
||||||
return setCurrentUserImpl(personName);
|
{
|
||||||
|
// Get the person name and use that as the current user to line up with permission checks
|
||||||
|
String personName = (String) nodeService.getProperty(userNode, ContentModel.PROP_USERNAME);
|
||||||
|
return setCurrentUserImpl(personName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set using the user name
|
||||||
|
return setCurrentUserImpl(userName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -30,8 +30,14 @@ import net.sf.jooreports.openoffice.connection.OpenOfficeConnection;
|
|||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.i18n.I18NUtil;
|
import org.alfresco.i18n.I18NUtil;
|
||||||
|
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
|
||||||
|
import org.alfresco.repo.content.transform.ContentTransformerRegistry;
|
||||||
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.quartz.Job;
|
||||||
|
import org.quartz.JobDataMap;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,6 +50,8 @@ public class OpenOfficeConnectionTester extends AbstractLifecycleBean
|
|||||||
{
|
{
|
||||||
private static final String INFO_CONNECTION_VERIFIED = "system.openoffice.info.connection_verified";
|
private static final String INFO_CONNECTION_VERIFIED = "system.openoffice.info.connection_verified";
|
||||||
private static final String ERR_CONNECTION_FAILED = "system.openoffice.err.connection_failed";
|
private static final String ERR_CONNECTION_FAILED = "system.openoffice.err.connection_failed";
|
||||||
|
private static final String ERR_CONNECTION_LOST = "system.openoffice.err.connection_lost";
|
||||||
|
private static final String ERR_CONNECTION_REMADE = "system.openoffice.err.connection_remade";
|
||||||
|
|
||||||
private static Log logger = LogFactory.getLog(OpenOfficeConnectionTester.class);
|
private static Log logger = LogFactory.getLog(OpenOfficeConnectionTester.class);
|
||||||
|
|
||||||
@ -104,26 +112,15 @@ public class OpenOfficeConnectionTester extends AbstractLifecycleBean
|
|||||||
*/
|
*/
|
||||||
private synchronized void checkConnection()
|
private synchronized void checkConnection()
|
||||||
{
|
{
|
||||||
PropertyCheck.mandatory(this, "connection", connection);
|
|
||||||
String connectedMessage = I18NUtil.getMessage(INFO_CONNECTION_VERIFIED);
|
String connectedMessage = I18NUtil.getMessage(INFO_CONNECTION_VERIFIED);
|
||||||
if (connection.isConnected())
|
boolean connected = testAndConnect();
|
||||||
|
OpenOfficeConnectionTesterJob.wasConnected = Boolean.valueOf(connected);
|
||||||
|
if (connected)
|
||||||
{
|
{
|
||||||
// the connection is fine
|
// the connection is fine
|
||||||
logger.info(connectedMessage);
|
logger.debug(connectedMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// attempt to make the connection
|
|
||||||
try
|
|
||||||
{
|
|
||||||
connection.connect();
|
|
||||||
// that worked
|
|
||||||
logger.info(connectedMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (ConnectException e)
|
|
||||||
{
|
|
||||||
// no luck
|
|
||||||
}
|
|
||||||
// now we have to either fail or report the connection
|
// now we have to either fail or report the connection
|
||||||
String msg = I18NUtil.getMessage(ERR_CONNECTION_FAILED);
|
String msg = I18NUtil.getMessage(ERR_CONNECTION_FAILED);
|
||||||
if (strict)
|
if (strict)
|
||||||
@ -135,4 +132,110 @@ public class OpenOfficeConnectionTester extends AbstractLifecycleBean
|
|||||||
logger.warn(msg);
|
logger.warn(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean testAndConnect()
|
||||||
|
{
|
||||||
|
PropertyCheck.mandatory(this, "connection", connection);
|
||||||
|
if (connection.isConnected())
|
||||||
|
{
|
||||||
|
// the connection is fine
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// attempt to make the connection
|
||||||
|
try
|
||||||
|
{
|
||||||
|
connection.connect();
|
||||||
|
// that worked
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (ConnectException e)
|
||||||
|
{
|
||||||
|
// No luck
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quartz job that checks an OpenOffice connection.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
* @since 2.1.2
|
||||||
|
*/
|
||||||
|
public static class OpenOfficeConnectionTesterJob implements Job
|
||||||
|
{
|
||||||
|
private static volatile Boolean wasConnected;
|
||||||
|
|
||||||
|
public OpenOfficeConnectionTesterJob()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the connection.
|
||||||
|
* @see OpenOfficeConnectionTester#checkConnection()
|
||||||
|
*/
|
||||||
|
public synchronized void execute(JobExecutionContext context) throws JobExecutionException
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Synchronized just in case of overzelous triggering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
JobDataMap jobData = context.getJobDetail().getJobDataMap();
|
||||||
|
// Get the connecion tester
|
||||||
|
Object openOfficeConnectionTesterObj = jobData.get("openOfficeConnectionTester");
|
||||||
|
if (openOfficeConnectionTesterObj == null || !(openOfficeConnectionTesterObj instanceof OpenOfficeConnectionTester))
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("OpenOfficeConnectionJob data must contain valid 'openOfficeConnectionTester' reference");
|
||||||
|
}
|
||||||
|
OpenOfficeConnectionTester openOfficeConnectionTester = (OpenOfficeConnectionTester) openOfficeConnectionTesterObj;
|
||||||
|
|
||||||
|
// Get the extractor and transformer registries. These are not mandatory.
|
||||||
|
Object metadataExractorRegistryObj = jobData.get("metadataExractorRegistry");
|
||||||
|
MetadataExtracterRegistry metadataExtracterRegistry = null;
|
||||||
|
if (metadataExractorRegistryObj != null && (metadataExractorRegistryObj instanceof MetadataExtracterRegistry))
|
||||||
|
{
|
||||||
|
metadataExtracterRegistry = (MetadataExtracterRegistry) metadataExractorRegistryObj;
|
||||||
|
}
|
||||||
|
Object contentTransformerRegistryObj = jobData.get("contentTransformerRegistry");
|
||||||
|
ContentTransformerRegistry contentTransformerRegistry = null;
|
||||||
|
if (contentTransformerRegistryObj != null && (contentTransformerRegistryObj instanceof ContentTransformerRegistry))
|
||||||
|
{
|
||||||
|
contentTransformerRegistry = (ContentTransformerRegistry) contentTransformerRegistryObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now ping the connection. It doesn't matter if it fails or not.
|
||||||
|
boolean connected = openOfficeConnectionTester.testAndConnect();
|
||||||
|
// Now log, if necessary
|
||||||
|
if (OpenOfficeConnectionTesterJob.wasConnected == null)
|
||||||
|
{
|
||||||
|
// This is the first pass
|
||||||
|
}
|
||||||
|
else if (OpenOfficeConnectionTesterJob.wasConnected.booleanValue() == connected)
|
||||||
|
{
|
||||||
|
// Nothing changed since last time
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (connected)
|
||||||
|
{
|
||||||
|
// This is reported as a warning as admins must be aware that it is bouncing
|
||||||
|
logger.info(I18NUtil.getMessage(ERR_CONNECTION_REMADE));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.error(I18NUtil.getMessage(ERR_CONNECTION_LOST));
|
||||||
|
}
|
||||||
|
// The value changed so ensure that the registries are bounced
|
||||||
|
if (metadataExtracterRegistry != null)
|
||||||
|
{
|
||||||
|
metadataExtracterRegistry.resetCache();
|
||||||
|
}
|
||||||
|
if (contentTransformerRegistry != null)
|
||||||
|
{
|
||||||
|
contentTransformerRegistry.resetCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Record the state
|
||||||
|
OpenOfficeConnectionTesterJob.wasConnected = Boolean.valueOf(connected);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user