Spring Data MongoTemplate not throwing DataAccessException - spring-data

I am trying to learn MongoDB and in the same time write a simple REST application using Spring framework.
I have a simple model:
#Document
public class Permission extends documentBase{
#Indexed(unique = true)
private String name;
public Permission(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Then I have a simple DAO:
#Repository
#Transactional
#Profile({"production","repositoryTest","mongoIntegrationTest"})
public class DaoImpl implements DAO {
#Autowired
protected MongoTemplate mongoTemplate;
public <T> T addObject(T object) {
mongoTemplate.insert(object);
return object;
}
The I have my integration tests:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:mvc-dispatcher-servlet.xml", classpath:IntegrationContext.xml"},loader = TestXmlContextLoader.class)
#ActiveProfiles("mongoIntegrationTest")
public class RepositoryIntegrationTest extends AccountTestBase{
#Autowired DAO repository;
#Autowired WebApplicationContext wac;
#Test
public void AddPermission() {
Permission permission_1 = new Permission("test");
Permission permission_2 = new Permission("test");
repository.addObject(permission_1);
repository.addObject(permission_2);
}
}
My configuration:
<!-- MongoDB host -->
<mongo:mongo host="${mongo.host.name}" port="${mongo.host.port}"/>
<!-- Template for performing MongoDB operations -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"
c:mongo-ref="mongo" c:databaseName="${mongo.db.name}"/>
I am expecting that, on adding "permission_2" their would be a exception thrown from MongoDB, which would be translated by Spring,, and catched as a DataAccessException in the DAO.
Looking at the log files from MongoDb I can see that a duplicated exception is thrown but it never reaches my DAO.
So,, I guess I am doing something wrong,,, but at the moment,, I am blind to my own misstakes.
//lg

Make sure you configure the WriteConcern of the MongoTemplate to something non-default (e.g. WriteConcern.SAFE). By default MongoDB is in fire-and-forget mode and does not throw exceptions on index violations or server errors in general.

Still struggling with this.
Finnally I succeded to get the exeption translation working. MongoDb throws a exception which is translated to Spring Data exception.
Now I am stuck with another problem.
My DAO shown above has also the following code:
#ExceptionHandler(DataAccessException.class)
public void handleDataAccessException(DataAccessException ex) {
// For debug only
DataAccessException test = ex;
test.printStackTrace();
}
I was expecting this code to catch the exception thrown,, but this is not the case.
Why not?
//lasse

Related

JPA. Update dml doesn't work with #Transactional annotation

I tryng to perform an an unpdate within a UnitTest method with org.springframework.transaction.annotation.Transactional annotation, but it seems that the update doesn't work.
If I remove #Transactional annotation on the method, the update works succesfully but it is visible for all others tests too.
Could you please indicate to me where I'm wrong?
I need the update effective only within the method with the #Transactional annotation and not visible for all others methods.
I'm using
Srping boot v 2.6.6. to start the application. I use JPA and Oracle Data Base.
This is my repository class where I use native query.
#Repository
#Transactional
public interface EsercentiRepository extends JpaRepository<EsercentiEntity, Long> {
// Update
#Modifying
#Query(value="update esercenti set sslfl=:sslfl where id_conv=:idConv", nativeQuery=true)
public void updateSslFlagByIdViaQuery(#Param("idConv") long idConv, #Param("sslfl") String sslfl);
// select
#Query(value="select id_conv,c_code,vendor_id,pos_id,abi_code,funzioni,ds,pos_id_sia,rifmer3d,sslfl "
+ " from esercenti where id_conv=:idConv", nativeQuery=true)
public EsercentiEntity getEsercentiByIdConvViaQuery(#Param("idConv") long idConv);
}
This is my Service class.
#Service
#Transactional
public class EsercentiServices implements IEsercenti {
#Autowired
private EsercentiRepository esercentiRepository;
#Override
public void updateSslFlagByIdViaQuery(long idConv, String sslfl) throws Exception {
esercentiRepository.updateSslFlagByIdViaQuery(idConv, sslfl);
}
#Override
public EsercentiEntity getEsercentiByIdConvViaQuery(long idConv) throws Exception {
return esercentiRepository.getEsercentiByIdConvViaQuery(idConv);
}
}
And this is my SpringBootTest class located in the 'test' directory where I use Junit 5 to perform Functional tests.
#EnableTransactionManagement
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
#ExtendWith(SpringExtension.class)
#SpringBootTest
class TestGpay extends BaseServiceTester {
#Autowired
private IEsercenti esercentiServices;
[..]
#Test
#Transactional
public void TestDirectAuthGpayWithPayload(TestInfo testInfo) {
try {
// [..... Some codes]
EsercentiEntity ese = esercentiServices.getEsercentiByIdConvViaQuery(7826L);
esercentiServices.updateSslFlagByIdViaQuery(7826L, "Y");
EsercentiEntity ese2 = esercentiServices.getEsercentiByIdConvViaQuery(7826L);
// Send the http request. I expect to find the data changed on DB as per above update, but it is not.
WebUtils.HttpResponse response = netsJsonClient(endPoint, "POST", jsonObjectRequest.toString(), merId, merIdKsig);
// If I cancel the #Transactoinal annotation on the method level, the update is ok,
// but it is a global update and not only related to this database session.
} catch (Exception e) {
e.printStackTrace();
closeDriver(driver);
fail("Exception on test case " + testInfo.getDisplayName() + " Full Error:" + e);
}
}
Thanks in advance.

How to use together spring-data-ldap and spring-security-ldap?

In a project using spring-security-ldap I need to perform some LDAP queries and I added spring-data-ldap. Suddenly I can't connect anymore to the embedded LDAP registry and I get:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'run': Invocation of init method failed; nested exception is org.springframework.ldap.CommunicationException: localhost:8389; nested exception is javax.naming.CommunicationException: localhost:8389 [Root exception is java.net.ConnectException: Connexion refusée (Connection refused)]
Here is the security config which work as expected:
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests().antMatchers("/admins").hasRole("ADMINS")
.antMatchers("/users").hasRole("USERS")
.anyRequest().fullyAuthenticated()
.and()
.httpBasic();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.userSearchBase("ou=people")
.userSearchFilter("uid={0}")
.groupSearchBase("ou=groups")
.groupSearchFilter("uniqueMember={0}")
.contextSource(contextSource())
.passwordCompare()
.passwordAttribute("userPassword");
}
#Bean
public DefaultSpringSecurityContextSource contextSource()
{
log.info("*** SpringSecurityConfig.contextSource(): Inside contextSource");
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
Arrays.asList("ldap://localhost:8389/"), "dc=toto,dc=com");
contextSource.afterPropertiesSet();
return contextSource;
}
}
Now, if I want to use spring-data-ldap, I add this:
#Repository
public interface MyLdapRepository extends LdapRepository<LdapUser>
{
}
#Entry(base="ou=users", objectClasses = {"person", "inetOrgPerson", "top"})
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
public class LdapUser
{
#Id
private Name id;
#Attribute(name = "uid")
private String uid;
#Attribute(name = "cn")
private String cn;
}
And I try to make some queries, for example:
#SpringBootApplication
#Slf4j
public class Run extends SpringBootServletInitializer
{
#Autowired
private RdfLdapRepository rdfLdapRespository;
public static void main(String[] args)
{
SpringApplication.run(Run.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
{
return builder.sources(Run.class);
}
#PostConstruct
public void setup()
{
log.info("### setup(): the LDIF file has been loaded");
Iterable<LdapUser> users = rdfLdapRespository.findAll();
users.forEach(user -> log.info("\"### setup(): names {}", user.getUid()));
}
}
I get Connection Refused. Commenting out the setup() method, everything works as expected again. I suspect some missmatch between the Ldaptemplate used by spring-data-ldap and the DefaultSpringSecurityContextSource in the security config.
Does anyone know what might be the problem here ?
Many thanks in advance;
Kind regards,
Nicolas
Problem solved. Everything was due to the fact that another instance of the Spring embedded LDAP directory server was running in the same Tomcat container. I'm not sure how this interacted with the spring-data-ldap and why it appeared only in this context but using the following command was capital as it helped me understand the issue:
> lsof -i:8389
This way I noticed that another LDAP embedded server was active and I understood why (consequence of repetitive deployment on the same Tomcat container).

Morphia, Embed Mongo and Spring. Address already in use

I am trying use MongoDB, Morphia and Spring and test it, so I started use Embedded Mongo.
When I had only one DAO to persist I did not had any problem with my tests, however, in some cases I needed use more than one DAO, and in that cases my injected Datasore give me an problem: addr already in use.
My Spring Test Database Configuration is this:
#Configuration
public class DatabaseMockConfig {
private static final int PORT = 12345;
private MongodConfigBuilder configBuilder;
private MongodExecutable mongodExecutable;
private MongodProcess mongodProcess;
#Bean
#Scope("prototype")
public MongodExecutable getMongodExecutable() {
return this.mongodExecutable;
}
#Bean
#Scope("prototype")
public MongodProcess mongodProcess() {
return this.mongodProcess;
}
#Bean
public IMongodConfig getMongodConfig() throws UnknownHostException, IOException {
if (this.configBuilder == null) {
configBuilder = new MongodConfigBuilder().version(Version.Main.PRODUCTION).net(new Net(PORT, Network.localhostIsIPv6()));
}
return this.configBuilder.build();
}
#Autowired
#Bean
#Scope("prototype")
public Datastore datastore(IMongodConfig mongodConfig) throws IOException {
MongodStarter starter = MongodStarter.getDefaultInstance();
this.mongodExecutable = starter.prepare(mongodConfig);
this.mongodProcess = mongodExecutable.start();
MongoClient mongoClient = new MongoClient("localhost", PORT);
return new Morphia().createDatastore(mongoClient, "morphia");
}
#Autowired
#Bean
#Scope("prototype")
public EventDAO eventDAO(final Datastore datastore) {
return new EventDAO(datastore);
}
#Autowired
#Bean
#Scope("prototype")
public EditionDAO editionDAO(final Datastore datastore) {
return new EditionDAO(datastore);
}
}
And my DAO classes are similar to that
#Repository
public class EventDAO {
private final BasicDAO<Event, ObjectId> basicDAO;
#Autowired
public EventDAO(final Datastore datastore) {
this.basicDAO = new BasicDAO<>(Event.class, datastore);
}
...
}
My test class is similar to that:
#ContextConfiguration(classes = AppMockConfig.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class EventDAOTest {
#Autowired
private EventDAO eventDAO;
#Autowired
private MongodExecutable mongodExecutable;
#Autowired
private MongodProcess mongodProcess;
#Rule
public ExpectedException expectedEx = ExpectedException.none();
#After
public void tearDown() {
this.mongodProcess.stop();
this.mongodExecutable.stop();
}
...
}
I use prototype scope to solve problem with singleton and make sure that my mock database is clean when I start my test, after that I stop mongod process and mongod executable.
However since I need use more than one DAO I receive that error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'editionDAO' defined in class br.com.mymusicapp.spring.DatabaseMockConfig: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.mongodb.morphia.Datastore]: :
Error creating bean with name 'datastore' defined in class br.com.mymusicapp.spring.DatabaseMockConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.mongodb.morphia.Datastore]:
Factory method 'datastore' threw exception; nested exception is java.io.IOException: Could not start process: ERROR: listen(): bind() failed errno:98 Address already in use for socket: 0.0.0.0:12345
2015-01-04T01:05:04.128-0200 [initandlisten] ERROR: addr already in use
I know what the error means, I just do not know how can I design my Configuration to solve that. As last option I am considering install a localhost MongoDB just for tests, however I think could be a better solution
That is based on the embedded mongod by flapdoodle, right?
If you want to run multiple tests in parallel (could be changed via JUnit annotations, but it's probably faster in parallel), you cannot use a single, hardcoded port. Instead, let the embedded process select an available port automatically.

Whether Replication Possible With Spring Data Couchbase?

HI i just want to know whether the XDCR replication is possible with spring data Couchbase. If possible how can i achieve that .please help .
My Code Sample
//configuration class
#Configuration
public class ApplicationConfig {
#Bean
public CouchbaseClient couchbaseClient() throws IOException {
return new CouchbaseClient(Arrays.asList(URI
.create("http://localhost:8091/pools")), "xxxw", "");
}
#Bean
public CouchbaseTemplate couchbaseTemplate() throws IOException {
return new CouchbaseTemplate(couchbaseClient());
}
}
#Document
public class Person {
#Field
String name;
#Id
String id;
public Person(final String personId, final String personname,
final int personIdAge, final JSONObject personData) {
this.id = personId;
this.name = personname;
this.age = personIdAge;
this.body = personData;
}
}
//main Test class
public class Test {
public static void main(String s[]) {
try {
ApplicationContext context = new AnnotationConfigApplicationContext(
ApplicationConfig.class);
CouchbaseTemplate template = context.getBean("couchbaseTemplate",
CouchbaseTemplate.class);
} catch (Exception e) {
e.printStackTrace();
}
}
How can i achieve repltcaion to elastic search index through spring data couchbase . with this sample classes ..??
Using elastic search in Couchbase is not dependent on what you client application looks like or whether or not it uses Spring. As long as you are storing data in Couchbase in JSON format things should work fine.
Setting up elastic search is more of an operations task than a development task. Take a look at the instructions at the link below and then run you application code as is. If you have configured everything properly then your data should end up in elastic search.
http://docs.couchbase.com/couchbase-elastic-search/

Inject Spring bean within RESTEasy Resource at Test time

Within a Unit/Integration Test, I'm trying to use the RESTEasy embedded server TJWSEmbeddedJaxrsServer or POJOResourceFactory inorder to simulate through a MockHttpRequest.get("/data") a resource call for test purpose.
My problem is that based on the use of the server or the Resource factory I'm not able to have a non null instance of spring beans which are injected normally within my resources.
Here's some code for clarification, thanks in advance.
Spring application context :
<context:annotation-config />
<context:component-scan base-package="com.cdcfast.service" />
<bean id="simpleResource" class="com.cdcfast.rest.SimpleResource" />
SimpleResource.java :
#Component
#Path("/data")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class SimpleResource {
#Autowired
private SimpleService service;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Data> getData() {
return MockDataBase.getInstance().getRows();
}
Unit Test :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:/test/spring/testApplicationContext.xml" })
public class FakeTest {
private Dispatcher dispatcher;
#Before
public void before() {
dispatcher = MockDispatcherFactory.createDispatcher();
POJOResourceFactory noDefaults = new POJOResourceFactory(SimpleResource.class);
dispatcher.getRegistry().addResourceFactory(noDefaults);
}
#Test
public void aTestThatAlwaysPass() throws URISyntaxException {
MockHttpRequest request = MockHttpRequest.get("/data");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assertions.assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
Assertions.assertThat(response.getContentAsString()).isNotNull().isNotEmpty();
}
}
I've had this before because the RESTEasy factories create the POJO rather than Spring so they don't get wired up which can be worked around in the full container but is less easy in a test. The best way around this is to get a handle to your POJO once the factory creates it and then do something similar to this:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(myPojo);
I personally ended up having Spring create the RESTEasy beans using the RESTEasy-Spring plugin and then launching my tests using Jetty, not sure if that is an option for you though.
I exeprienced same problem and i'have solved in similar way as James did:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring-context-test.xml" })
public class TestMyService {
Dispatcher dispatcher;
private String username = "user";
#Autowired
ApplicationContext context;
#Before
public void setUp() {
MyService g = new MyService(); //rest service with #autowired spring beans
context.getAutowireCapableBeanFactory().autowireBean(g);
dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(g);
}
#Test
public void TestRest() {
MockHttpRequest request;
try {
request = MockHttpRequest.get("/rest/service").header("LOGON_USER", username);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertTrue("Error, unexpected status code: " + response.getStatus(), response.getStatus() == 200);
LoggerFactory.getLogger(this.getClass()).info("********** " + response.getContentAsString());
} catch (URISyntaxException e) {
Log.error(e.getMessage(), e);
fail(e.getMessage());
}
}
}