I'd like to do the following and I'm not quite sure if I'm not wasting my time: I'm trying to run ActiveMQ embedded broker inside a weblogic servlet. The idea is, that the clients will be able to connect to JMS via http and the embedded broker would serve the requests. I know that this is a crazy idea, but it's a legacy application and a lot of the client code depends on JMS. The idea is just to switch the connection string and add libraries to clients.
It works fine when I create the tcp connection, but I have no idea how to map a servlet to the internal broker
The restrictions are these:
No changes in weblogic configuration(like datasources, bridges, JMS etc)
No Spring
HTTP only
This is the servlet definition from web.xml:
<servlet>
<servlet-name>ActiveMQServlet</servlet-name>
<servlet-class>com.mycompany.ActiveMQServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ActiveMQ</servlet-name>
<url-pattern>/servlet/ActiveMQServlet</url-pattern>
</servlet-mapping>
Important parts of the servlet:
public class ActiveMQServlet extends HttpServlet {
private BrokerService broker;
private static final Log log = LogFactory.getLog(new Object() {
}.getClass().getEnclosingClass());
#Override
public void init() throws ServletException {
log.info("Load activeMQ");
// configure the broker
try {
TransportConnector connector = new TransportConnector();
connector.setUri(new URI(ACTIVE_MQ_URL));
broker = new BrokerService();
broker.addConnector(connector);
broker.start();
log.info("ActiveMQ loaded succesfully");
} catch (Exception e) {
log.error("Unable to load ActiveMQ!", e);
}
}
#Override
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("Received call.... ");
log.info("Request: "+request);
//todo
}
The problem is I have no idea how to do a request/response mapping between the service method of the servlet and internal ActiveMQ broker.
Another thing is, that I'm trying to solve some classpath clashes caused by slf4j which is used by ActiveMQ broker and already spent some hours on it.
But maybe I'm just doing something which is impossible/really stupid.
When I'm trying to connect via simple client, I'm getting this exception
javax.jms.JMSException: Could not connect to broker URL: http://localhost:8888/myapp/servlet/ActiveMQServlet. Reason: java.io.IOException: Failed to perform GET on: http://localhost:8888/myapp/servlet/ActiveMQServlet as response was: Not Found
at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:35)
at org.apache.activemq.ActiveMQConnectionFactory.createActiveMQConnection(ActiveMQConnectionFactory.java:293)
at org.apache.activemq.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:191)
...
Caused by: java.io.IOException: Failed to perform GET on: http://localhost:8888/myapp/servlet/ActiveMQServlet as response was: Not Found
at org.apache.activemq.transport.http.HttpClientTransport.doStart(HttpClientTransport.java:275)
at org.apache.activemq.util.ServiceSupport.start(ServiceSupport.java:55)
While doing this, the code in the servlet is not executed. The servlet path is fine. If I open it in the browser, I got empty page and log message.
I'm using ActiveMQ 5.8.0 and Weblogic 10.3.6
I'm trying to do something similar, and something I have found is MessageListenerServlet - I'm still searching for examples on setting it up (I'm relatively new at dealing with servlets) but I think it's supposed to allow this sort of thing. Though I think you're main application needs to be the one setting up the BrokerService at startup, and the servlet just needs to reference the address. In my case, we aren't using websphere, but the general premise of "talk to an embedded ActiveMQ instance from external sources via http" is the same.
Related
Current Solution
I have a Java server (Tomcat) setup issue that I'm hoping someone can provide some guidance on. Currently my web application is a single-server that has a Java backend running on Tomcat 8.5. To handle Websocket connections, I keep a Map of all the javax.websocket.Session passed in the onOpen() method.
#ServerEndpoint("/status")
public class StatusMessenger
{
private static ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap();
#OnOpen
public void onOpen(Session session) throws Exception
{
String sessionId = session.getRequestParameterMap().get("sessionId").get(0);
sessions.put(session.getId(), session);
}
My application only broadcasts messages to all users, so the broadcast() in my code simply loops through sessions.values() and sends the message through each javax.websocket.Session.
public static void broadcast(String event, String message)
{
for (Session session: sessions.values())
{
// send the message
}
}
I'm not even sure that's the correct way to handle Websockets in Tomcat, but it's worked for me for years, so I assume it's acceptable.
The Problem
I want to now horizontally scale out my application on AWS to multiple servers. For the most part my application is stateless and I store the regular HTTP session information in the database. My problem is this static Map of javax.websocket.Session - it's not stateless, and there's a different Map on each server, each with their own list of javax.websocket.Sessions.
In my application, the server code in certain situations will need to broadcast out a message to all the users. These events may happen on any server in this multi-server setup. The event will trigger the broadcast() method which loops through the javax.websocket.Sessions. However, it will only loop through the sessions in it's own Map.
How do I get the multi-server application to broadcast this message to all websocket connections stored across all the servers in the setup? The application works fine on a single-server (obviously) because there's only 1 list of websocket sessions. In other words, how do I write a stateless application that needs to store the websocket connections so it can communicate with them later?
I found 2 alternative solutions for this...
In my load balancer I put a rule to route all paths with /{my websocket server path} to 1 server so that all the Sessions were on the same server.
Use a 3rd party web push library like Pusher (http://pusher.com)
I'm trying to run example from http://www.baeldung.com/spring-remoting-amqp, even when I set up the connection to the dedicated vhost to my RabbitMQ broker, I can only send the request from client (I see it in RabbitMQ UI), but I never get the answer from the server.
The server seems to bean the service (the returning Impl class) with getBeanDefinitionNames(), but I definitly do not see those beans on the client side. I use annotations to set up beans, not the .xml file.
So the question is - why my client is not seeing the Server beans, I discover it more a less in following way:
#Autowired
private ApplicationContext appContext;
public GetResponse get(String id) {
Service service = appContext.getBean(Service.class);
System.out.println(service.ping());
return new GetResponse();
}
The answer which I get on the level of webservice is:
{
"timestamp": "2018-02-01T10:09:00.809Z",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.remoting.RemoteProxyFailureException",
"message": "No reply received from 'toString' with arguments '[]' - perhaps a timeout in the template?",
"path": "/v3/app/r"
}
Service:
public interface Service extends Serializable{
String ping();
}
Service Impl:
public class ServiceImpl implements Service {
#Override
public String ping() {
System.out.println("ponged");
return "pong";
}
#Override
public String toString() {
return "to string";
}
EDITED + BOUNTY
In the link you can find extracted modules which I want to connect together. I suppose that it is still about 'not seeing' the beans from one module in the second one.
The action can be trigerd with GET http://localhost:8081/v3/app/u The RabbitMQ settings has to be adjusted to your set-up.
https://bitbucket.org/herbatnic/springremotingexample/overview
I think you shouldn't set the routing key in your client, in amqpFactoryBean (and the one you set seems invalid):
https://bitbucket.org/herbatnic/springremotingexample/src/b1f08a5398889525a0b1a439b9bb4943f345ffd1/Mod1/src/main/java/simpleremoting/mod1/messaging/Caller.java?at=master&fileviewer=file-view-default
Did you try to run their example?
https://github.com/eugenp/tutorials/tree/master/spring-remoting/remoting-amqp
Just stumbled upon this question 3 years later.. trying to run the Baeldung example!
I tried debugging the issue and as far as I can tell, something internal in the AMQP implementation of spring remoting is not using the correct Routing Key when sending the client message, meaning the payload arrives at the broker and is never put into the queue for processing, we then timeout after 5s (default) on the client.
I tried the other answer by Syl to remove the routingKey however it doesn't seem to allow us to create a binding without one, and even when creating a binding directly on the broker management page (without a routing key) it doesn't route the messages.
I have not managed to make the example work, however I found a blog post on fatalerrors.org that shows a custom implementation of the AmqpProxyFactoryBean and it has custom handling for the routing key, this one works.
I've create this gist with the example that is working for me in case the blog post above goes under.
One other thing to note is that on the Baeldung example they are using a DirectExchange, while here we are using a TopicExchange.
I am developing a REST API application using Spring-Boot. It turns that when I start the server (using the embedded tomcat) and I start sending requests to my API, I get the expected responses. But, lets say I wait for 30 minutes before send another request, at that time I get an org.springframework.transaction.CannotCreateTransactionException with root cause java.net.SocketTimeoutException: Read timed out.
My application connects to a remote MySQL server data base.
My WebApplicationStarter class looks looks is the following:
#Configuration
#EnableAutoConfiguration
#ComponentScan("monitec")
public class WebApplicationStarter extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(WebApplicationStarter.class);
}
public static void main(String[] args) throws Exception {
ApplicationContext context = SpringApplication.run(WebApplicationStarter.class, args);
}
#Bean
public SessionFactory sessionFactory(HibernateEntityManagerFactory hemf) {
return hemf.getSessionFactory();
}
#Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(connector ->
((AbstractProtocol) connector.getProtocolHandler()).setConnectionTimeout(10000));
factory.setPort(7543);//TODO: Replace this hardcoded value by a system preference
factory.setSessionTimeout(20000);
// configure some more properties
return factory;
}
}
My application.properties is the following:
# Thymeleaf
spring.thymeleaf.cache: false
# Data Source
spring.datasource.url=jdbc:mysql://hostname:8888/schema_name
spring.datasource.username=xxxxxxxxx
spring.datasource.password=xxxxxxxxxxx
# Hibernate
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
#spring.jpa.generate-ddl=true
#spring.jpa.hibernate.ddl-auto=create
I have research several posts and haven't been able to solve my problem. I also set the sessionTimeout to "-1" to make it infinite but it didn't work. I don't know if perhaps it is the MySQL server the one that is closing the connection, and if that's the case I would like to know how to make my application open a new one when a new http request arrive to the server. For now I have not enable any kind of security, I mean I do not require authentication from any client calling my REST API, I'll do it in the future, but for now it is not necessary.
Thank you in advance, I am open to any suggestions and improvements you can give me. If you need my REST Controller code, let me know and I'll post it.
PD: I am using POST MAN REST CLIENT to test my application.
EDIT: I always get the read timed out exception and I can't send any more requests to the server unless I restart the server. This means that after the exception, every request that I send from any client, I keep receiving the exception and the only way to get the expected result is by restarting the application (the embedded tomcat)
I have narrowed the issue to be a problem with Spring-Boot autoconfig managing the connection pool. And I confirmed my diagnose after reading this post
https://aodcoding.wordpress.com/2015/05/22/handling-connection-pool-issues-in-spring-boot/
So, I solve the problem by adding connection pool properties, I decided not to used the C3P0 ones described in the article that I mentioned, but instead I used spring boot ones as follows:
spring.datasource.max-active=50
spring.datasource.initial-size=5
spring.datasource.max-idle=10
spring.datasource.min-idle=5
spring.datasource.test-while-idle=true
spring.datasource.test-on-borrow=true
spring.datasource.validation-query=SELECT 1 FROM DUAL
spring.datasource.time-between-eviction-runs-millis=5000
spring.datasource.min-evictable-idle-time-millis=60000
And as far as I can tell, the problem is solved. I have wait for long time and re send requests to my service and I am getting proper responses.
Next step for me is start enabling spring security configuration to secure the REST services.
Hope this help to any one having same issue I had. Because if you see the exception, is not very clear that the problem is due to connection pool, you would try to hit the problem following the wrong direction.
I have a java web application, that has a very strict security requirement. on Start-up it tries to configure the web container to force it use SSL session id to establish the http session id. If it fails in this configuration I want the app to stop and not process an request. I can use System.exit() as I am doing in the sample below, but I am wondering if there is a nice way to do this without killing the JVM. Also a security manager can get in the way of System.exit()
I am using tomcat 7.
public class SessionTrackingModeListener implements ServletContextListener
{
#Override
public void contextInitialized(ServletContextEvent event)
{
try
{
ServletContext context = event.getServletContext();
EnumSet<SessionTrackingMode> modes = EnumSet.of(SessionTrackingMode.SSL);
context.setSessionTrackingModes(modes);
System.out.println("SSL based Session tracking enabled");
} catch (Exception e)
{
System.err.println("Unable to setup SSL based session tracking");
e.printStackTrace();
System.exit(1);
// Question How do I get the container to not start the app without using exit?
}
}
#Override
public void contextDestroyed(ServletContextEvent event)
{
}
}
First of all I think it is not a good idea to use
System.exit(1)
in a servlet environment, since some web containers may use a security manager to prevent your webapp to kill the entire server.
The servlet spec is not strict about what happens when you throw a runtime exception in the contextInitialized function. In my experience the servlet containers abort the startup of the webapp, but again it may depend on your container so you should test it.
If you are using plain old servlets it maybe a good choice to create a ServletFilter which checks whether the security constraints are ok or redirect the request to an error page.
Would you please explain more about "want the app to stop and not process an request".You mean yours web application OR web container.Do you want to show some human readable message on web application when there is some problem in container to user ?.As when there is exception the tomcat does not get started start.You have to start it again .
Following class implements Struts org.apache.struts.action.PlugIn, i have overriden the init method.
My web server is tomcat.
Following method does an RMI Look up and stores the server address and port in an object when the first request comes in
public void init(ActionServlet servlet, ModuleConfig moduleConfig) {
try
{
// Some Code
remoteObject= lookup(strIpAddress, strRMIPort);
}
catch (Exception e)
{
System.out.println("Remote server not configured")
}
}
The look up code does an remote lookup to our server and fetches the adrress and port.
I have a problem over here. When my 'remote server' is not started, I get an exception in my tomcat console, but I need to forward to another error.jsp page , since the Init method is a void method, and am not sure how to get the request and response object in this method, am struck over here.
Can you kindly tell, how will I delegate to an error page when there is an exception in that code?
A plugin's init method should be called when the module's action servlet spins up.
Redirecting to an error page from within a plugin doesn't make a lot of sense: they're internal framework classes and have no direct user visibility. Instead an application-wide flag (or similar mechanism) should be set, and an error can be reported to an app user based on its presence (or value, etc.)
To indicate web app startup failures a fatal log message is appropriate. You could also send an email, update a dashboard, send a JMS message, etc. depending on monitoring mechanisms in place.