Spring: Is there a simple non-web tutorial? - java

I'm attempting to create a Spring application (NOT web application) to perform some simple tasks. Eventually they will hook up with some other Spring apps around the network, but for now I'm keeping it simple. I have a CheckForNewItems class (extending Timer) which is configured to run every 10 seconds.
I can confirm it runs by calling it programmatically:
public class Tester {
public static ApplicationContext context;
private void loadContext() {
String filename = "beans.xml";
context = new FileSystemXmlApplicationContext(filename);
}
public static void main(String[] args) {
Tester test = new Tester();
test.loadContext();
CheckNewItemsTask task = (CheckNewItemsTask)context.getBean("checkNewItemsTask");
}
}
Running this works as expected, task.run() gets called every 10 seconds. Now I need to work out how to deploy this to either a JBoss or Tomcat server, in such a way that it automatically starts running the task.
Most of the tutorials I've found only describe how to get Spring MVC and servlets running, not a standalone application. Does anyone know better?
Cheers, Rob.

You don't need JBoss or Tomcat to do that. If the app is headless and you have no intention of adding a UI, consider jsvc for unix or procrun on windows. If you need the ability to monitor and control an app and do not need a proper UI for doing that, you might want to look at JMX. This will work on a daemon without the rest of the Java EE stack.
If you have a maven project and want an easy way to turn it into a deployable daemon app, you can use maven appassembler to automate the process of creating a deployable daemon, setting up a directory structure of the app, scripts to start and stop, libraries and config files.

You need a servlet that is set to autostart on deployment. The servlet can then call into your "Tester" class to trigger your "standalone" initialization process.
If you don't have a servlet (or potentially some other server related process) reference your code, then your initialization process will never be run.

Related

Is it possible to modify Clojure code running as a Spring component on a remote server and download the modified code using the REPL?

Imagine I have the following setup:
A Spring Boot application.
Inside it is a Camunda workflow engine.
There are multiple components (#org.springframework.stereotype.Component) inside that application that are written in Clojure and are used by the Camunda workflow engine.
I heard that allegedly it is possible to modify the code of a Clojure application without restarting it.
So, I want to
modify the code of those components (without restarting the application),
add new components to the application (without restarting the application), and
after I'm done prototyping, download the current version of all components there.
The idea is that I prototype the components from the REPL until they work as designed. This means that the Camunda workflow engine will use the components modified by my actions on the REPL.
Then, I download the current version of the components in the application (so that they are not lost when the application is shut down). This code is then cleaned up, refactored, covered by unit tests and put under version control.
Question:
Is it theoretically possible to implement such workflow with Clojure (not necessarily out of the box)?
Are there any known limitations which would make such workflow absolutely impossible?
Update 1
Found following projects which prima facie allow you to interact with Java code using a REPL:
spring-boot-bugger
spring-repl
However, I don't know whether you can use them to change the code.
This is a very rough and simple example of how to do this. I have
dropped Camunda here because I don't think it's actually relevant.
The approach here:
Create Spring services, that delegate to actual Clojure code.
Make sure to load a "current good version" of that code into your
process
Start a "server REPL" to allow overriding
The full project can be found here
Provide a service, that delegates to some Clojure code:
#Service
class ClojureBackedService {
BigDecimal add(BigDecimal a, BigDecimal b) {
Clojure.var('net.ofnir.repl', 'add').invoke(a, b)
}
}
Start a REPL and load some "good initial setup":
#Service
class ClojureRepl {
#PostConstruct
def init() {
def require = Clojure.var('clojure.core', 'require')
require.invoke(Clojure.read('net.ofnir.repl'))
Clojure.var('clojure.core.server', 'start-server').invoke(
Clojure.read("{:port 5555 :name spring-repl :accept clojure.core.server/repl}")
)
}
#PreDestroy
def destroy() {
Clojure.var('clojure.core.server', 'stop-server').invoke(
Clojure.read("{:name spring-repl}")
)
}
}
For showing how this works, provide a web endpoint, that adds two
numbers:
#RestController
class MathController {
private final ClojureBackedService backend
MathController(ClojureBackedService backend) {
this.backend = backend
}
#PostMapping
def add(BigDecimal a, BigDecimal b) {
backend.add(a, b)
}
}
Run the application and do a first test:
$ curl -da=5 -db=5 localhost:8080
10
Looks good. Now connect to the REPL and replace the add function:
$ telnet localhost 5555
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
user=> (ns net.ofnir.repl)
nil
net.ofnir.repl=> (defn add [a b] (* a b))
#'net.ofnir.repl/add
No call the endpoint again:
$ curl -da=5 -db=5 localhost:8080
25
And there you have your spring service changed at runtime.
If you requirement is to update the code which gets executed as part of e.g. a service task without restarting the process engine, then you may want to look into decoupling the process engine from this code completely. The external service task pattern allows you to do this:
https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/
Instead of deploying the code into the Camunda runtime, you start an external worker in a separate runtime (e.g. JVM). Some benefits include:
process engine and worker are decoupled. Engine does not need to know where worker is located. the worker call the engine when it is available.
thread management is externalized to worker. Engine thread pool does not need to be scaled.
worker can be implemented in programming language of choice
worker can be upgraded independently (even different version of the worker could run at the same time, e.g. during rolling upgrades)
worker can be scaled independent of process engine
Also see for instance:
Camunda Platform Tutorial: Building and Running a JavaScript External Task in Camunda Run
https://github.com/camunda/awesome-camunda-external-clients
https://camunda.com/blog/2021/03/external-task-client-spring-bootified/

Call method after JBoss deploy

I want to have some method run, only after my WAR has been deployed to JBoss.
The problem: Currently I am using #PostConstruct to load saved schedules from the DB. The problem is that I am creating instances of the Schedulers from this method, which in turn is starting the Quartz schedulers, which is stopping JBoss from completing the deploy.
If there are no schedules to load, my WAR deploys fine, but if there are schedules they are causing the deploy to fail, because JBoss is "waiting" for the schedules to actually complete.
Is there some way to delay the method call until after it is fully deployed?
Or alternatively, is it possible to make Async calls on the Server (from the Server code)?
Java EE specification heavily refrain any thread manipulation outside facility provided by the application server.
You shouldn't in any case use a container manged thread in an infinite loop; the container expect the thread to be returned. Thread creation can still be done without too much collateral damage (if you don't put several apps on the server as the container won't be able to manage the resources between all the applications) but the any container thread must be returned.
In the new Jboss 7 there is some Java EE scheduling facilities (#Scheduling and timer, with possible persistent timer). A quick search show some example how to run Quartz in JBoss 7: How to enable Quartz scheduling in Jboss AS 7.0?
In older JBoss more advanced integration exist (JCA integration is the only standard way to get finer thread management). Use google to find them.
Wouldn't the simple ServletContextListener solve the problem for you? Just implement whatever you need in contextInitialized method.
In JBoss 7, there is a management api.
Maybe you can use it to check if the server is started (with a singleton and a TimerService).
Sample code:
ModelControllerClient client = ModelControllerClient.Factory.create(InetAddress.getByName("localhost"), 9999);
ModelNode op = new ModelNode();
op.get(ClientConstants.OP).set("read-attribute");
op.get(ClientConstants.NAME).set("server-state");
ModelNode returnVal = client.execute(op);
if(StringUtils.equals(returnVal.get("result").toString(), "\"running\"")){
LOGGER.info("Server running, init start actions");
timer.cancel();
}else{
LOGGER.info("Server not running, wait");
}

Deploying Terminal Base Java Application

I'm migrating from .net to java and I'm not yet used in java application deployment. I'm used in deploying console base application that acts as a stand alone application , a mixed of tcp and udp servers with custom protocol.
I have a requirement that my ported .net application to java must be deployed inside tomcat or glass fish ( no embedding stuff ). I really don't know what technology I must used. I've been searching the net but my understanding is that tomcat is like IIS and for web application only and glass fish is somewhat an application server for hosting web application too. Can I really run my java console base application inside tomcat or glass fish? Can someone point out a good tutorials for this kind of stuff? Thanks!
EDIT 1
Ok got the reason why I need to deploy my app in tomcat/glassfish. I need to provide a web ui for my application since I'm currently using the console for user input. Now my application will not just support a custom tcp/udp server inside but also web functionality for management. Any suggestion how I can implement this is greatly appreciated, I just don't know yet what java api/technology to start with.
I am not sure why your requirement says that you need to run an application using a servlet container . I don't think at least based on your description your application fits servlet container programming model.
As long as you create an entry point, I think you can launch your application from command line either using java or javaw,
But If you are unable to change the requirement on the deployment to tomcat, You can do this by using a servlet to launch your application, I would read up on these things
Servlet
Deploying Servlet to tomcat
Here is one way you could do using a servlet and deploy this to tomcat
public class LaunchServlet extends HttpServlet
{
private static final long serialVersionUID = 4277145689972356257L;
//this method is run as tomcat starts up this servlet
public void init() throws ServletException
{
try
{
System.out.println("Launching my application...");
new Thread(new ApplicationLauncher()).start();
System.out.println("Launched my application successfully. ");
}
catch(Exception e)
{
throw new RuntimeException("Fail Fast: Unable to launch exception.");
}
}
class ApplicationLauncher implements Runnable
{
public void run()
{
//start you applicaton here
}
}
}

Deploying Java Application As Servlet

I have a java application that up until now was run as a stand alone java application (i.e. executable jar). I now need to deploy it in Tomcat as a servlet. It doesn't need to actually process any HTTP requests though, but it needs to be started using tomcat.
What are the steps required to convert the project so that it can be deployed in Tomcat? I'm using maven as a build tool and Java 1.5.
I understand that you want to run this app on server's startup. The best way would be implementing ServletContextListener and run the app in the contextInitialized() method. E.g.
public class Config implements ServletContextListener {
private YourApp yourApp;
public void contextInitialized(ServletContextEvent event) {
yourApp = new YourApp();
yourApp.start();
}
public void contextDestroyed(ServletContextEvent event) {
yourApp.shutdown();
}
}
Register this in web.xml as follows:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
That's it. No need to wrap it in flavor of a HttpServlet as you aren't going to fire HTTP requests on it.
You however need to ensure that it runs in its own thread, otherwise it would block the startup. If it doesn't, then wrap it in a Runnable and execute it using ExecutorService.
I'm assuming that your app is continuously running and you have an app/web server already (e.g. Tomcat/Jetty), such that it's making your life easy to deploy into it. Given that, you need to:
extend an AbstractHttpServlet class and in particular the init() method. This would start your app.
build a web.xml that references this and sets the load-on-startup attribute to 1 (or at least non-zero)
build a .war from this and deploy it
Step 2 ensures that the init() method is called upon deployment/server reboot, and so you don't have to respond to HTTP requests (a normal startup trigger for a servlet).
It may be simpler and more appropriate to use something like javaservicewrapper, and wrap it up to be a Windows service or similar.

Start / stop a web application from itself?

I've made a web application using Java, Struts and running over Apache Server and Tomcat. It would be very useful to be able to restart the application from the web. I don't want to restart Tomcat, only this webapp, the same way as Tomcat Manager does it.
Do you know how can I do it? If not, any way to simulate that behaviour (reload config.properties, make Hibernate init process, etc)?
Thank you a lot.
I took a quick look at the source code for the Tomcat Manager. It looks like there's a class that's part of the Tomcat source called "Container Servlet". From the javadocs:
A ContainerServlet is a servlet
that has access to Catalina internal
functionality, and is loaded from the
Catalina class loader instead of the
web application class loader.
A ContainerServlet automatically gets passed a wrapper that can be used to get the Context and Deployer -- and the Deployer has helpful methods such as start(String contextPath) and stop(String contextPath) that will do what you want.
So, what I think you would need to do is write your own servlet that inherits from ContainerServlet, and configure Tomcat to load your servlet using the Catalina class loader (look at how the Manager is configured to see how). Note that this is probably not going to be an option for you in a hosted environment.
Then your servlet could have, say, a button you press to reload the application. I recommend putting password-protection of some kind in front of that. :)
Just hit the URLs
http://<username>:<password>#<hostname>:<port>/manager/stop?path=/<context path>
to stop and
http://<username>:<password>#<hostname>:<port>/manager/start?path=/<context path>
to start. That simulates you using the manager application.
Tomcat Manager offers an http interface to start/stop an application and other tasks. There are Ant tasks that you can use to easily access these operations.

Categories