DATADOC-216 - Added ability to configure a WriteConcern on DB level.
Added WriteConcern property to MongoDbFactory and expose it through the db-factory namespace element.
This commit is contained in:
@@ -4,10 +4,27 @@ import org.springframework.dao.DataAccessException;
|
||||
|
||||
import com.mongodb.DB;
|
||||
|
||||
/**
|
||||
* Interface for factories creating {@link DB} instances.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
*/
|
||||
public interface MongoDbFactory {
|
||||
|
||||
/**
|
||||
* Creates a default {@link DB} instance.
|
||||
*
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
DB getDb() throws DataAccessException;
|
||||
|
||||
/**
|
||||
* Creates a {@link DB} instance to access the database with the given name.
|
||||
*
|
||||
* @param dbName must not be {@literal null} or empty.
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
DB getDb(String dbName) throws DataAccessException;
|
||||
|
||||
}
|
||||
|
||||
@@ -13,17 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mongodb.config;
|
||||
|
||||
import static org.springframework.data.mongodb.config.BeanNames.*;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.authentication.UserCredentials;
|
||||
import org.springframework.data.mongodb.core.MongoFactoryBean;
|
||||
@@ -31,13 +32,19 @@ import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* {@link BeanDefinitionParser} to parse {@code db-factory} elements into {@link BeanDefinition}s.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
String id = element.getAttribute("id");
|
||||
if (!StringUtils.hasText(id)) {
|
||||
id = DB_FACTORY;
|
||||
@@ -47,55 +54,70 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class);
|
||||
|
||||
// UserCredentials
|
||||
BeanDefinitionBuilder userCredentialsBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserCredentials.class);
|
||||
String username = element.getAttribute("username");
|
||||
if (StringUtils.hasText(username)) {
|
||||
userCredentialsBuilder.addConstructorArgValue(username);
|
||||
} else {
|
||||
userCredentialsBuilder.addConstructorArgValue(null);
|
||||
}
|
||||
String password = element.getAttribute("password");
|
||||
if (StringUtils.hasText(password)) {
|
||||
userCredentialsBuilder.addConstructorArgValue(password);
|
||||
} else {
|
||||
userCredentialsBuilder.addConstructorArgValue(null);
|
||||
}
|
||||
|
||||
// host and port
|
||||
String host = element.getAttribute("host");
|
||||
if (!StringUtils.hasText(host)) {
|
||||
host = "localhost";
|
||||
}
|
||||
String port = element.getAttribute("port");
|
||||
if (!StringUtils.hasText(port)) {
|
||||
port = "27017";
|
||||
}
|
||||
|
||||
// Database name
|
||||
String dbname = element.getAttribute("dbname");
|
||||
if (!StringUtils.hasText(dbname)) {
|
||||
dbname = "db";
|
||||
}
|
||||
|
||||
// com.mongodb.Mongo object
|
||||
String mongoRef = element.getAttribute("mongo-ref");
|
||||
|
||||
if (!StringUtils.hasText(mongoRef)) {
|
||||
//Create implicit com.mongodb.Mongo object and register under generated name
|
||||
BeanDefinitionBuilder mongoBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoFactoryBean.class);
|
||||
mongoBuilder.addPropertyValue("host", host);
|
||||
mongoBuilder.addPropertyValue("port", port);
|
||||
mongoRef = BeanDefinitionReaderUtils.registerWithGeneratedName(mongoBuilder.getBeanDefinition(), parserContext.getRegistry());
|
||||
}
|
||||
dbFactoryBuilder.addConstructorArgValue(new RuntimeBeanReference(mongoRef));
|
||||
dbFactoryBuilder.addConstructorArgValue(dbname);
|
||||
dbFactoryBuilder.addConstructorArgValue(userCredentialsBuilder.getBeanDefinition());
|
||||
|
||||
return dbFactoryBuilder.getRawBeanDefinition();
|
||||
|
||||
}
|
||||
|
||||
String mongoRef = element.getAttribute("mongo-ref");
|
||||
if (!StringUtils.hasText(mongoRef)) {
|
||||
mongoRef = registerMongoBeanDefinition(element, parserContext);
|
||||
}
|
||||
|
||||
// Database name
|
||||
String dbname = element.getAttribute("dbname");
|
||||
if (!StringUtils.hasText(dbname)) {
|
||||
dbname = "db";
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class);
|
||||
dbFactoryBuilder.addConstructorArgValue(new RuntimeBeanReference(mongoRef));
|
||||
dbFactoryBuilder.addConstructorArgValue(dbname);
|
||||
|
||||
BeanDefinition userCredentials = getUserCredentialsBeanDefinition(element);
|
||||
if (userCredentials != null) {
|
||||
dbFactoryBuilder.addConstructorArgValue(userCredentials);
|
||||
}
|
||||
|
||||
ParsingUtils.setPropertyValue(element, dbFactoryBuilder, "write-concern", "writeConcern");
|
||||
|
||||
return dbFactoryBuilder.getBeanDefinition();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a default {@link BeanDefinition} of a {@link Mongo} instance and returns the name under which the
|
||||
* {@link Mongo} instance was registered under.
|
||||
*
|
||||
* @param element must not be {@literal null}.
|
||||
* @param parserContext must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private String registerMongoBeanDefinition(Element element, ParserContext parserContext) {
|
||||
|
||||
BeanDefinitionBuilder mongoBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoFactoryBean.class);
|
||||
ParsingUtils.setPropertyValue(element, mongoBuilder, "host");
|
||||
ParsingUtils.setPropertyValue(element, mongoBuilder, "port");
|
||||
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(mongoBuilder.getBeanDefinition(),
|
||||
parserContext.getRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link BeanDefinition} for a {@link UserCredentials} object.
|
||||
*
|
||||
* @param element
|
||||
* @return the {@link BeanDefinition} or {@literal null} if neither username nor password given.
|
||||
*/
|
||||
private BeanDefinition getUserCredentialsBeanDefinition(Element element) {
|
||||
|
||||
String username = element.getAttribute("username");
|
||||
String password = element.getAttribute("password");
|
||||
|
||||
if (!StringUtils.hasText(username) && !StringUtils.hasText(password)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder userCredentialsBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserCredentials.class);
|
||||
userCredentialsBuilder.addConstructorArgValue(StringUtils.hasText(username) ? username : null);
|
||||
userCredentialsBuilder.addConstructorArgValue(StringUtils.hasText(password) ? password : null);
|
||||
|
||||
return userCredentialsBuilder.getBeanDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.mongodb.ServerAddress;
|
||||
abstract class ParsingUtils {
|
||||
|
||||
/**
|
||||
* Parses the mongo replica-set element.
|
||||
* Parses the mongo replica-set element.
|
||||
* @param parserContext the parser context
|
||||
* @param element the mongo element
|
||||
* @param mongoBuilder the bean definition builder to populate
|
||||
@@ -42,17 +42,17 @@ abstract class ParsingUtils {
|
||||
if (StringUtils.hasText(replicaSetString)) {
|
||||
ManagedList<Object> serverAddresses = new ManagedList<Object>();
|
||||
String[] replicaSetStringArray = StringUtils.commaDelimitedListToStringArray(replicaSetString);
|
||||
for (int i = 0; i < replicaSetStringArray.length; i++) {
|
||||
String[] hostAndPort = StringUtils.delimitedListToStringArray(replicaSetStringArray[i], ":");
|
||||
for (String element2 : replicaSetStringArray) {
|
||||
String[] hostAndPort = StringUtils.delimitedListToStringArray(element2, ":");
|
||||
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(ServerAddress.class);
|
||||
defBuilder.addConstructorArgValue(hostAndPort[0]);
|
||||
defBuilder.addConstructorArgValue(hostAndPort[1]);
|
||||
serverAddresses.add(defBuilder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
if (!serverAddresses.isEmpty()) {
|
||||
mongoBuilder.addPropertyValue("replicaSetSeeds", serverAddresses);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
@@ -63,8 +63,9 @@ abstract class ParsingUtils {
|
||||
*/
|
||||
static boolean parseMongoOptions(ParserContext parserContext, Element element, BeanDefinitionBuilder mongoBuilder) {
|
||||
Element optionsElement = DomUtils.getChildElementByTagName(element, "options");
|
||||
if (optionsElement == null)
|
||||
return false;
|
||||
if (optionsElement == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder optionsDefBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(MongoOptionsFactoryBean.class);
|
||||
@@ -75,12 +76,12 @@ abstract class ParsingUtils {
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "max-wait-time", "maxWaitTime");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "connect-timeout", "connectTimeout");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "socket-timeout", "socketTimeout");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "socket-keep-alive", "socketKeepAlive");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "socket-keep-alive", "socketKeepAlive");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "auto-connect-retry", "autoConnectRetry");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "write-number", "writeNumber");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "write-number", "writeNumber");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "write-timeout", "writeTimeout");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "write-fsync", "writeFsync");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "slave-ok", "slaveOk");
|
||||
setPropertyValue(optionsElement, optionsDefBuilder, "slave-ok", "slaveOk");
|
||||
|
||||
|
||||
|
||||
@@ -94,4 +95,19 @@ abstract class ParsingUtils {
|
||||
builder.addPropertyValue(propertyName, attr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property with the given attribute name on the given {@link BeanDefinitionBuilder} to the value of the
|
||||
* attribute with the given name.
|
||||
*
|
||||
* @param element must not be {@literal null}.
|
||||
* @param builder must not be {@literal null}.
|
||||
* @param attrName must not be {@literal null} or empty.
|
||||
*/
|
||||
static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attrName) {
|
||||
String attr = element.getAttribute(attrName);
|
||||
if (StringUtils.hasText(attr)) {
|
||||
builder.addPropertyValue(attrName, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
import com.mongodb.DB;
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.mongodb.CannotGetMongoDbConnectionException;
|
||||
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoOptions;
|
||||
import com.mongodb.ServerAddress;
|
||||
@@ -93,7 +92,7 @@ public class MongoFactoryBean implements FactoryBean<Mongo>, PersistenceExceptio
|
||||
|
||||
if (host == null) {
|
||||
|
||||
logger.warn("Property host not specified. Using default configuration");
|
||||
logger.debug("Property host not specified. Using default configuration");
|
||||
mongo = new Mongo();
|
||||
|
||||
} else {
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.springframework.data.mongodb.core;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -10,25 +25,29 @@ import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.WriteConcern;
|
||||
|
||||
/**
|
||||
* Factory to create {@link DB} instances from a {@link Mongo} instance.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
|
||||
/**
|
||||
* Logger, available to subclasses.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Mongo mongo;
|
||||
private String databaseName;
|
||||
private final Mongo mongo;
|
||||
private final String databaseName;
|
||||
private String username;
|
||||
private String password;
|
||||
private WriteConcern writeConcern;
|
||||
|
||||
/**
|
||||
* Create an instance of SimpleMongoDbFactory given the Mongo instance and
|
||||
* database name
|
||||
* Create an instance of {@link SimpleMongoDbFactory} given the {@link Mongo} instance and database name.
|
||||
*
|
||||
* @param mongo Mongo instance, not null
|
||||
* @param databaseName Database name, not null
|
||||
* @param mongo Mongo instance, must not be {@literal null}.
|
||||
* @param databaseName database name, not be {@literal null}.
|
||||
*/
|
||||
public SimpleMongoDbFactory(Mongo mongo, String databaseName) {
|
||||
Assert.notNull(mongo, "Mongo must not be null");
|
||||
@@ -38,32 +57,50 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of SimpleMongoDbFactory given the Mongo instance,
|
||||
* database name, and username/password
|
||||
* Create an instance of SimpleMongoDbFactory given the Mongo instance, database name, and username/password
|
||||
*
|
||||
* @param mongo Mongo instance, not null
|
||||
* @param databaseName Database name, not null
|
||||
* @param userCredentials username and password
|
||||
* @param mongo Mongo instance, must not be {@literal null}.
|
||||
* @param databaseName Database name, must not be {@literal null}.
|
||||
* @param userCredentials username and password must not be {@literal null}.
|
||||
*/
|
||||
public SimpleMongoDbFactory(Mongo mongo, String databaseName,
|
||||
UserCredentials userCredentials) {
|
||||
public SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials userCredentials) {
|
||||
this(mongo, databaseName);
|
||||
this.username = userCredentials.getUsername();
|
||||
this.password = userCredentials.getPassword();
|
||||
}
|
||||
|
||||
public DB getDb() throws DataAccessException {
|
||||
Assert.notNull(mongo, "Mongo must not be null");
|
||||
Assert.hasText(databaseName, "Database name must not be empty");
|
||||
return MongoDbUtils.getDB(mongo, databaseName, username,
|
||||
password == null ? null : password.toCharArray());
|
||||
/**
|
||||
* Configures the {@link WriteConcern} to be used on the {@link DB} instance being created.
|
||||
*
|
||||
* @param writeConcern the writeConcern to set
|
||||
*/
|
||||
public void setWriteConcern(WriteConcern writeConcern) {
|
||||
this.writeConcern = writeConcern;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.MongoDbFactory#getDb()
|
||||
*/
|
||||
public DB getDb() throws DataAccessException {
|
||||
return getDb(databaseName);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.MongoDbFactory#getDb(java.lang.String)
|
||||
*/
|
||||
public DB getDb(String dbName) throws DataAccessException {
|
||||
Assert.notNull(mongo, "Mongo must not be null");
|
||||
Assert.hasText(dbName, "Database name must not be empty");
|
||||
return MongoDbUtils.getDB(mongo, dbName, username,
|
||||
password == null ? null : password.toCharArray());
|
||||
|
||||
Assert.hasText(dbName, "Database name must not be empty.");
|
||||
|
||||
DB db = MongoDbUtils.getDB(mongo, dbName, username, password == null ? null : password.toCharArray());
|
||||
|
||||
if (writeConcern != null) {
|
||||
db.setWriteConcern(writeConcern);
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,5 +109,4 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {
|
||||
public void destroy() throws Exception {
|
||||
mongo.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ The password to use when connecting to a MongoDB server.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="writeConcern" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.springframework.data.mongodb.config;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.xml.XmlBeanFactory;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MongoDbFactoryParser}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class MongoDbFactoryParserIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void parsesWriteConcern() {
|
||||
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/db-factory-bean.xml"));
|
||||
BeanDefinition definition = factory.getBeanDefinition("first");
|
||||
|
||||
List<PropertyValue> values = definition.getPropertyValues().getPropertyValueList();
|
||||
assertThat(values, hasItem(new PropertyValue("writeConcern", "SAFE")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsDbFactoryBean() {
|
||||
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/db-factory-bean.xml"));
|
||||
factory.getBean("first");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<mongo:db-factory id="first" write-concern="SAFE"/>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user