I have a spring boot application, where I have used the pattern of extending AbstractCassandraConfiguration to bootstrap Cassandra. It alls works beautifully. I now want to turn on query logging, so i have attempted to implement this pattern:
https://github.com/spring-projects/spring-boot/issues/7312#issuecomment-358448437
I do this by updating my AbstractCassandraConfiguration implementation to:
1. Be a #Configuration class
2. Adding the Bean QueryLogger Bean
But then at runtime, I start getting the NoHostAvailableException.
As the before and after code are v similar, ill just paste it all and indicate the new, breaking code.
I can feel connection is getting corrupted, but also feel my pattern is so standard, not sure what is going on!!! Any tips appreciated. Thanks
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;
import org.springframework.data.cassandra.config.SchemaAction;
import com.datastax.driver.core.AuthProvider;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.PlainTextAuthProvider;
import com.datastax.driver.core.QueryLogger;
#Configuration // **** NEW ****
public class CassandraConfig extends AbstractCassandraConfiguration {
#Value("${spring.data.cassandra.contact-points:placeholder}")
private String contactPoints;
#Value("${spring.data.cassandra.port:0000}")
private int port;
#Value("${spring.data.cassandra.keyspace-name:placeholder}")
private String keySpace;
#Value("${spring.data.cassandra.username}")
private String username;
#Value("${spring.data.cassandra.password}")
private String password;
#Value("${spring.data.cassandra.schema-action}")
private String schemaAction;
// removed all the #Override getters for above props
// **** NEW ****
#Bean
public QueryLogger queryLogger(Cluster cluster) {
QueryLogger queryLogger = QueryLogger.builder().build();
cluster.register(queryLogger);
return queryLogger;
}
}
Exception:
[dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.data.cassandra.CassandraConnectionFailureException: Query; CQL [SELECT * FROM my_table;]; All host(s) tried for query failed (tried: cassandradb01.AAA.test.XXXX.com/10.18.51.15:9042 (com.datastax.driver.core.exceptions.BusyPoolException: [cassandradb01.AAA.test.XXXX.com/10.18.51.15] Pool is busy (no available connection and timed out after 5000 MILLISECONDS))); nested exception is com.datastax.driver.core.exceptions.NoHostAvailableException: All host(s) tried for query failed (tried: cassandradb01.AAA.test.XXXX.com/10.18.51.15:9042 (com.datastax.driver.core.exceptions.BusyPoolException: [cassandradb01.AAA.test.XXXX.com/10.18.51.15] Pool is busy (no available connection and timed out after 5000 MILLISECONDS)))] with root cause
Related
I tried to use #Aspect for logging all requests and responses the code is running fine and successful with a simple spring boot project with a common configuration and not have any init methods or DB call.
The below code is not working that includes init configuration and DB call for required data used #EnableTransactionManagement its getting Error while use #Aspect while running my application
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
public class MyContextClass implements ApplicationListener<ContextRefreshedEvent>{
#Autowire
private MyServiceRepository myServiceRepo;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// Added Db call
CommonUtils.setYears(myServiceRepo.getTotalYears());
}
}
#Aspect logging code
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
#Aspect
#Component
public class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Autowired
private HttpServletRequest request;
#Autowired
private HttpServletResponse response;
#Pointcut("within(#org.springframework.stereotype.Repository *)"
+ " || within(#org.springframework.stereotype.Service *)"
+ " || within(#org.springframework.web.bind.annotation.RestController *)")
public void springBeanPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the
// advices.
log.info("springBeanPointcut");
}
/**
* Pointcut that matches all Spring beans in the application's main packages.
*/
#Pointcut("within(com.test.mypackage..*)")
public void applicationPackagePointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the
// advices.
log.info("applicationPackagePointcut");
}
#Around("applicationPackagePointcut() && springBeanPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
String methodParameterNames = methodSignature.getMethod().toString();
if (request ! = null) {
log.info(request.getRequestURL());
log.info(request.getMethod());
}
if (response != null) {
log.info(response.getRequestURL());
log.info(response.getStatus());
}
final StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
}
this code build success. but when I run the application below error display the error in setYears
2021-05-07 20:14:58 [restartedMain] ERROR o.s.boot.SpringApplication-[SpringApplication.java:856]-Application run failed
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:313)
at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:66)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:329)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:324)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:292)
at com.sun.proxy.$Proxy120.getRequestURL(Unknown Source)
at com.opl.ans.config.utils.LoggingAspect.logAround(LoggingAspect.java:137)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:624)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:72)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
Is it the right way to do it? Are there any suggestions? Please help.
Based on the error stack the issue is that the request instance is invalid. I would guess that the request instance autowired to the Aspect could be stale or rather not assosicated with the current thread. This means that request instance is not null and the check within logAround() method gives unexepected results.
RequestContextHolder.getRequestAttributes() would return null if no request attributes are currently bound to the thread. Modifying the
if (request ! = null) { ..} check with if (RequestContextHolder.getRequestAttributes() ! = null) {..} should fix the issue.
Also spring boot provides out of the box solutions to what you are currently attempting through AOP . Do check them out as well.
HTTP Tracing
Spring MVC Metrics
I have a main Spring Boot application in the top-level package that I launch, with different controllers in the children packages:
i.e.
ca.example.batch.MainBatchApplication
ca.example.batch.job1.Job1Controller (/batch/startJob1)
ca.example.batch.job2.Job2Controller (/batch/startJob2)
I am able to start the Spring Batches by going to the URL: http://localhost:8080/batch/startJob1 or http://localhost:8080/batch/startJob2.
However, in another package I have:
ca.example.batch.job3.Job3Controller
ca.example.batch.job3.Job3Application
... which is not a Spring Batch, but a Spring CommandLineRunner. I want to know if there is a way to launch MainBatchApplication without starting that CommandLineRunner automatically, but run it through the controller, i.e. http://localhost:8080/batch/startJob3.
The controller code I am looking at is:
package ca.example.batch.job3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class Job3Controller {
#RequestMapping("/batch/startJob3")
public String handle() throws Exception {
Job3Application app = new Job3Application();
Logger logger = LoggerFactory.getLogger(this.getClass());
logger.info("app: " + app);
String args = "";
app.run(args);
return "COMPLETE";
}
}
Job3Application is:
package ca.example.batch.job3;
import static java.lang.System.exit;
import java.util.List;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.Banner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Import;
import ca.example.batch.common.CommonLibraryReference;
import ca.example.batch.common.domain.WasHost;
import ca.example.batch.common.svc.WasHostService;
#SpringBootApplication
#Import(CommonLibraryReference.class)
public class Job3Application implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(Job3Application.class);
#Autowired
public DataSource dataSource;
#Autowired
public WasHostService wasHostService;
public Job3Application() {
}
public static void main(String[] args) throws Exception {
new SpringApplicationBuilder(Job3Application.class)
.web(WebApplicationType.NONE)
.bannerMode(Banner.Mode.OFF)
.run(args);
}
#Override
public void run(String... strings) throws Exception {
logger.info("Loading data...");
logger.info("wasHostService: " + wasHostService);
List<WasHost> hostList = wasHostService.findAll();
if (!hostList.isEmpty()) {
for (WasHost host : hostList) {
logger.info("hostname: " + host.getHostname());
}
} else {
logger.info("No hosts found in database. Aborting data collection.");
exit(0);
}
}
}
The error I get in the log is:
""2018-07-07 12:56:09 [http-nio-9191-exec-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring FrameworkServlet 'dispatcherServlet'
""2018-07-07 12:56:09 [http-nio-9191-exec-1] INFO c.e.b.job3.Job3Controller - app: ca.example.batch.job3.Job3Application#472d7ac
""2018-07-07 12:56:09 [http-nio-9191-exec-1] INFO c.e.b.job3.Job3Application - Loading data...
""2018-07-07 12:56:09 [http-nio-9191-exec-1] INFO c.e.b.job3.Job3Application - wasHostService: null
""2018-07-07 12:56:09 [http-nio-9191-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
"java.lang.NullPointerException: null
at ca.example.batch.job3.Job3Application.run(Job3Application.java:47)
at ca.example.batch.job3.Job3Controller.handle(Job3Controller.java:21)
... when I launch the controller request.
If this isn't the right way, please advise.
Basically, what I am trying to do is launch a main() from within a controller but use the MainBatchApplication runtime to run it (if that makes sense?). When the program is done, it send the return code back to the controller and shows in the browser.
Thank you,
Joey
ca.example.batch.MainBatchApplication is the main appliction you start.
So it scans all Components in package ca.example.batch. That means it should detect ca.example.batch.job3.Job3Application so you should be able to #Autowireit in the Job3Controller
this should work:
#Controller
public class Job3Controller {
private final Job3Application job3Application;
public Job3Controller (Job3Application job3Application){
this.job3Application = job3Application;
}
#RequestMapping("/batch/startJob3")
public String handle() throws Exception {
String[] args = ...
this.job3Application.run(args);
return "COMPLETE";
}
....
}
I use MS-SQL server for my spring application.
I have a store procedure, witch I want to execute from my app.
CREATE PROCEDURE dbo.spCheckPerson (
#inXML XML,
#outXML XML OUTPUT,
#inDebug BIT = 0 )
AS
--do sth
SET #outXML = '<person><name>Piotr</name></person>'
RETURN 0
I try to execute it by this DAO:
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Types;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.jdbc.support.xml.Jdbc4SqlXmlHandler;
public class PersonDAOImpl implements PersonDAO {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall spCheckPerson;
public PersonDAOImpl(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
public String checkPersonData(String personData) {
Jdbc4SqlXmlHandler sqlXmlHandler = new Jdbc4SqlXmlHandler();
spCheckPerson = new SimpleJdbcCall(jdbcTemplate)
.withoutProcedureColumnMetaDataAccess()
.withProcedureName("spCheckPerson")
.declareParameters(new SqlParameter("inXML", Types.SQLXML))
.declareParameters(new SqlOutParameter("outXML", Types.SQLXML));
Map<String, Object> result = spCheckPerson.execute(sqlXmlHandler.newSqlXmlValue("<test>Test</test>"));
return ((SQLXML) result.get("outXML")).getString();
}
}
, but I get error:
SEVERE: Servlet.service() for servlet [SpringDispatcher] in context with path
[/TestSpringProject] threw exception [Request processing failed; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: The TDS protocol stream is not valid.] with root cause
com.microsoft.sqlserver.jdbc.SQLServerException: The TDS protocol stream is not valid.
at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1667)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1654)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.throwInvalidTDS(SQLServerConnection.java:1635)
at com.microsoft.sqlserver.jdbc.TDSReader.throwInvalidTDS(IOBuffer.java:4747)
at com.microsoft.sqlserver.jdbc.TDSReader.readBytes(IOBuffer.java:5061)
at com.microsoft.sqlserver.jdbc.TDSReader.readWrappedBytes(IOBuffer.java:5082)
at com.microsoft.sqlserver.jdbc.TDSReader.readInt(IOBuffer.java:5023)
at com.microsoft.sqlserver.jdbc.TDSReader.readUnsignedInt(IOBuffer.java:5040)
at com.microsoft.sqlserver.jdbc.PLPInputStream.readBytesInternal(PLPInputStream.java:313)
at com.microsoft.sqlserver.jdbc.PLPInputStream.getBytes(PLPInputStream.java:104)
at com.microsoft.sqlserver.jdbc.PLPXMLInputStream.getBytes(PLPInputStream.java:509)
at com.microsoft.sqlserver.jdbc.SQLServerSQLXML.getString(SQLServerSQLXML.java:290)
at pl.test.dao.PersonDAOImpl.checkPersonData(PersonDAOImpl.java:34)
How should I properly receive this object? Which class should I use to cast value? What is TDS protocol? I tried to used method: getCharacterStream() from SQLXML but it gives the same exception.
i have created entity class from database then i have created session beans from entity class
and and when i am trying to test that my data reach the entity class i have created a junit test on the session beans class but i got lookup fail "Root exception is javax.naming.NameNotFoundException: EmpTableFacade!Beans.EmpTableFacade not found even if i used EJB injection
then i tried to test my code manually by creating class that use the session beans but the same exception here .
i think that the error from Deploying staff but i don't know where i did it wrong
this is session beans interface .
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package Beans;
import java.util.List;
import javax.ejb.Local;
/**
*
* #author HADDAD
*/
#Local
public interface EmpTableFacadeLocal {
void create(EmpTable empTable);
void edit(EmpTable empTable);
void remove(EmpTable empTable);
EmpTable find(Object id);
List<EmpTable> findAll();
List<EmpTable> findRange(int[] range);
int count();
}
and this is the test class
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package Controller;
import Beans.EmpTable;
import Beans.EmpTableFacade;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
*
* #author HADDAD
*/
public class Functionality {
List<EmpTable> l;
Context c=null;
{
try {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
// glassfish default port value will be 3700,
props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
//props.load(new FileInputStream("jndi.properties"));
c = new InitialContext(props);
} catch (NamingException ex) {
Logger.getLogger(Functionality.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void retrive() throws NamingException
{
EmpTableFacade empTableFacade=(EmpTableFacade)
c.lookup("java:global/employee/employee-ejb/EmpTableFacade!Beans.EmpTableFacade");
l= empTableFacade.findAll();
System.out.println(l.get(0).getName().toString());
}
public static void main(String[] args) throws NamingException
{
Functionality f=new Functionality();
f.retrive();
}
}
and this is the exception
Exception in thread "main" javax.naming.NamingException: Lookup failed for 'java:global/employee/employee-ejb/EmpTableFacade!Beans.EmpTableFacade' in SerialContext[myEnv={org.omg.CORBA.ORBInitialPort=3700, java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory, org.omg.CORBA.ORBInitialHost=localhost, java.naming.factory.url.pkgs=com.sun.enterprise.naming, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl} [Root exception is javax.naming.NameNotFoundException: EmpTableFacade!Beans.EmpTableFacade not found]
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:491)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:438)
at javax.naming.InitialContext.lookup(InitialContext.java:411)
at Controller.Functionality.retrive(Functionality.java:45)
at Controller.Functionality.main(Functionality.java:52)
You need to expose your bean with a Remote interface. A Local interface is available only for invocation within the same JVM, which it is not your case.
The JNDI name also looks wrong.
You donĀ“t specify which ejb or server version you are using which is important to solve this kind of problem, but I think this link can help you define the correct JNDI name entry.
Also take in mind that according to glassfish doc.
Each portable global JNDI name is printed out to the server.log during deployment.
when i run the following :
package NonServletFiles;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.sql.DataSource;
import javax.naming.*;
public class GetTagsFromDatabase {
public GetTagsFromDatabase() {
}
public String[] getTags() {
String tags[] = null;
try {
Context context = new InitialContext();
DataSource ds = (DataSource)context.lookup("java:comp/env/jdbc/photog"); // <<----- line 23
Connection connection = ds.getConnection();
String sqlQuery = "select NAMEOFTHETAG from tagcollection";
PreparedStatement statement = connection.prepareStatement(sqlQuery);
ResultSet set = statement.executeQuery();
int i = 0;
while(set.next()) {
tags[i] = set.getString("NameOfTheTag");
System.out.println(tags[i]);
i++;
}
}catch(Exception exc) {
exc.printStackTrace();
}
return tags;
}
public static void main(String args[]) {
new GetTagsFromDatabase().getTags(); // <<----- line 43
}
}
I get the following exceptions :
javax.naming.NamingException: Lookup failed for 'java:comp/env/jdbc/photog' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.url.pkgs=com.sun.enterprise.naming, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl} [Root exception is javax.naming.NamingException: Invocation exception: Got null ComponentInvocation ]
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:518)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:455)
at javax.naming.InitialContext.lookup(InitialContext.java:411)
at NonServletFiles.GetTagsFromDatabase.getTags(GetTagsFromDatabase.java:23)
at NonServletFiles.GetTagsFromDatabase.main(GetTagsFromDatabase.java:43)
Caused by: javax.naming.NamingException: Invocation exception: Got null ComponentInvocation
at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.getComponentId(GlassfishNamingManagerImpl.java:873)
at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.lookup(GlassfishNamingManagerImpl.java:742)
at com.sun.enterprise.naming.impl.JavaURLContext.lookup(JavaURLContext.java:172)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:498)
... 4 more
I don't know the reason for this exception,all other servlets that need to connect to the database with the url java:comp/env/jdbc/photog work fine.
The stacktrace hints that you're using Glassfish. Remove the java:comp/env/ part. It's the default JNDI context root already. Only in Tomcat you need to specify it explicitly. Also, you should be invoking this in webapp context, not as a plain Java Application with main().
Unrelated to the concrete problem, do you really need to get the DataSource everytime? I'd create a helper class which obtains it only once on webapp's startup or in a static initializer. It's application wide and threadsafe. Only the Connection indeed needs to be obtained (and closed! you're not closing it, so you're leaking DB resources) everytime you need to fire a SQL query.