We're using embedded Tomcat 7 to host a web application.
It works very well, with one slight exception.
The reason that we're using embedded Tomcat is because we need to operate on multiple platforms and our architect has made the call.
The Problem
We would like to give our users the ability to check for WAR updates while the wrapper/container Java application is running (live update).
Currently they have to restart our application which is much less desirable.
Basically, our code just checks a remote server for newer WAR files, if they exist they are downloaded.
The problem is that we can't seem to get the Tomcat server to shutdown or release lock that it has on the exploded WAR folders.
If we completely destroy the Tomcat instance, then we can deploy the WAR files and delete the exploded WAR folders, but Tomcat won't explode and host them until we completely kill the wrapper/container JAVA application and re-launch.
Once we re-launch, Tomcat explodes the WAR files and hosts the applications nicely.
What I'm Looking For
I'm hoping that there is a way to either un-deploy the application being updated or properly shutdown/stop the Tomcat server.
Code Sample
I simplified the below code sample by not including the implementation for the downloading as this works fine.
The Tomcat instance isn't public, but just made it so for ease of use.
Also, the sleep within the Thread was just thown-in to simplify the example.
The endless-loop was just put in for this sample.
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
...
public class Testing123
{
public static void main(String[] args)
{
final Testing123 test = new Testing123();
test.createTomcat();
test.downloadUpdate();
(new Thread())
{
public void run()
{
Thread.sleep(10000);
test.downloadUpdate();
}
}.start();
while (true)
{
// this loop is just for this example...
}
}
public Tomcat tomcat = null;
private String webappsPath = "c:\\tomcat\\webapps";
private void createTomcat()
{
this.tomcat = new Tomcat();
this.tomcat.setBaseDir(this.webappsPath);
this.tomcat.setPort("5959");
this.tomcat.getServer().setPort("5960");
this.tomcat.getServer().setShutdown("SHUTDOWN");
this.tomcat.getHost().setCreateDirs(true);
this.tomcat.getHost().setDeployIgnore(null);
this.tomcat.getHost().setDeployOnStartup(true);
this.tomcat.getHost().setAutoDeploy(true);
this.tomcat.getHost().setAppBase(this.webappsPath);
Context ctx = this.tomcat.addWebapp(null, "/app1", "APP1");
ctx.setLoader(new WebappLoader(Testing123.class.getClassLoader()));
ctx.setReloadable(true);
}
private boolean isTomcatRunning()
{
if ((this.tomcat == null) || (LifecycleState.STARTED == this.tomcat.getServer().getState()))
{
return true;
}
return false;
}
private void shutdownTomcat() throws Exception
{
if (this.isTomcatRunning())
{
if ((this.tomcat!= null) && (this.tomcat.getServer() != null))
{
this.tomcat.stop();
while ((LifecycleState.STOPPING == this.tomcat.getServer().getState()) || (LifecycleState.STOPPING_PREP == this.tomcat.getServer().getState()))
{
// wait for the server to stop.
}
}
}
}
private void startTomcat() throws Exception
{
if (this.isTomcatRunning())
{
if ((this.tomcat!= null) && (this.tomcat.getServer() != null))
{
try
{
this.tomcat.init();
while (LifecycleState.INITIALIZING == this.tomcat.getServer().getState())
{
// wait for the server to initialize.
}
}
catch (Throwable e)
{
// ignore
}
this.tomcat.start();
while ((LifecycleState.STARTING == this.tomcat.getServer().getState()) || (LifecycleState.STARTING_PREP == this.tomcat.getServer().getState()))
{
// wait for the server to start.
}
}
}
}
private void downloadUpdate()
{
this.shutdownTomcat();
// Download the WAR file and delete the exploded WAR folder...
this.startTomcat();
}
}
Related
I am trying to develop a tool that get a directory of maven artifacts and upload them to Nexus 3. The tool is working but I have a performance issue.
My program launch a separate maven process for each artifact that must be uploaded.I'm curious whether these could be batched somehow.
I am using the maven-invoker library for executing maven commands.
public class MavenUploader {
private final MavenDeployer mavenDeployer;
#Inject
public MavenUploader(MavenDeployer mavenDeployer) {
this.mavenDeployer = mavenDeployer;
}
#Override
public void uploadToRepository(Path pathToUpload) {
try (Stream<Path> files = Files.walk(pathToUpload)){
files.forEach(mavenDeployer::deployArtifact);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
This is the class that resposible to upload the artifacts
import org.apache.maven.shared.invoker.*;
public class MavenDeployer {
private final InvocationRequest invocationRequest;
private final Invoker invoker;
#Inject
public MavenDeployer(InvocationRequest invocationRequest,
Invoker invoker) {
this.invocationRequest = invocationRequest;
this.invoker = invoker;
}
public void deployArtifact(Path pathToPom, String commandToExecute) {
invocationRequest.setGoals(Collections.singletonList(commandToExecute));
InvocationResult invocationResult = invoker.execute(invocationRequest);
}
for each time that the deployArtifact method called new process is opened, There is a way to batch all the uploads to use the same process?
Not maven-deploy but you can use this if you'd like: https://github.com/DarthHater/nexus-repository-import-scripts
I think it accomplishes what you want to do.
I have a java project, which complied into an executable jar file v-agent-exe.jar. This jar is a log server, log rows is sent to it for processing.
I can execute it by using this command:
`java -jar v-agent-exe.jar -a watch -f config.ini`.
After executed, this jar file will create a ServerSocket at port 1235 and listen for incoming data from clients. After data received, the program will process the data and send the result back to the client. When I execute the jar from CMD windows, the processing is working perfect.
Now I am trying to wrap the Jar file as a Windows service (I am using Windows 10). I created a "Windows service project"
in Visual studio like below:
- Caller class have call() method to execute the jar file using process.
- AgentService is the service, which execute Caller->call() in another thread.
- Program is the main entry to load AgentService.
Caller.cs
public class Caller
{
static Process proc;
public Process GetProcess(){
return proc;
}
public void call() {
try
{
String dir = AppDomain.CurrentDomain.BaseDirectory;
proc = new Process
{
StartInfo = new ProcessStartInfo
{
WorkingDirectory = dir,
FileName = "java.exe",
Arguments = #"-jar v-agent-exe.jar -a watch -f config.ini",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
CreateNoWindow = true
}
};
proc.Start();
while (!proc.StandardError.EndOfStream)
{
string line = proc.StandardError.ReadLine();
}
}
catch (Exception ex) {
VAgentService.writeLog("Error when call process: " + ex.Message);
}
}
}
AgentService
public partial class AgentService : ServiceBase
{
private string jarPath;
private string iniPath;
static Process proc;
Caller caller;
public AgentService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
writeLog("On start");
try
{
caller = new Caller();
writeLog("Prepare to launch thread");
Thread t = new Thread(new ThreadStart(caller.call));
t.Start();
}
catch (Exception ex)
{
EventLog.WriteEntry("Demo error: " + ex.Message);
}
}
protected override void OnStop()
{
proc = caller.GetProcess();
if (proc != null && !proc.HasExited)
{
proc.Kill();
}
else
{
...
}
}
}
Program.cs
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(String[] args)
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new AgentService()
};
ServiceBase.Run(ServicesToRun);
}
}
After build the the service project, I have AgentService.exe.
I install it to my system using:
sc create VAgentLogging binpath= %CD%\AgentService.exe depend= lmhosts start= auto
After start the service in service.msc, I can telnet to port "1235" which the java process is listening (I am sure about
only the jar running in this port). According to the
log of java program, it still can received some part of data but seem like it cannot send back to client or something,
which cause the followed process cannot be done.
I think my problem is: the jar file can executed as standalone but somehow it sucks when wrapped under my service project.
I haven't posted the jar's code yet because I think the error is related to the Windows service project. If you need the java code, please tell me and I will update it here.
Any help would be appreciated.
I'm running an embedded Jetty 8 server that loads a few *.war files at startup:
for (File aWebAppDirectory : aWebAppDirectories) {
if (aWebAppDirectory.exists() && aWebAppDirectory.isDirectory()) {
for (File warFile : aWebAppDirectory.listFiles(new WarFileFilter())) { String basename = warFile.getName().replaceFirst("\\.war$", "");
fHandlers.addHandler(new WebAppContext(warFile.getAbsolutePath(), "/" + basename));
}
}
}
These war-files have some dependencies on a few classes that may or may not exist in the classpath.
Right now if one of my servlets is missing a dependency, my entire embedded Jetty service fails. (Because of NoClassDefFoundExceptions)
I need a method that allows me to catch exceptions for failing servlets and simply doesn't activate them. I'm looking for the same thing that TomCat does when a servlet fails to load: It still loads the rest.
I haven't found any solutions after some time searching on Google.
Anyone know how I can tackle this problem using embedded Jetty 8?
If anyone is curious how I fixed this, I simply made sure that all my servlets have a wrapper servlet that basically has no dependencies. The wrapper tries to initialize a delegate with dependencies and explicitly checks for NoClassDefFountException. If this happens, the delegate is set to null, and all calls to the wrapper interface will result in an exception.
So on a high level:
public class ServletWrapper extends HttpServlet{
private ServletDelegate fDelegate;
//If this is false, the delegate does not work, and we should not forward anything to it.
private boolean fAvailable = false;
public ServletWrapper(){
try{
fDelegate = new ServletDelegate();
fAvailable = true;
} catch (NoClassDefFoundError e) {
fAvailable = false;
}
}
#Override
protected void doPost( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException {
if ( !fAvailable || fDelegate==null ) {
response.sendError( HttpServletResponse.SC_SERVICE_UNAVAILABLE, LSP_MISSING_ERROR_MESSAGE );
return;
}
fDelegate.doPost(request,response);
}
}
It's simple enough, and it works.
I have been trying to get a Windows service running from my JAR file. WinRun4j seems to be able to do the job, but I can't get it to work. I am especially finding it quite difficult to debug. I tried several methods for logging (writing to a .txt file, WinRun4j's EventLog class) but I can't seem to generate any output.
The service installs fine (eventually..) and I can start it. It should start a Jetty server that generates an XML file that can be reached over HTTP. The app works for a stand-alone version, just not for the service. The service is started, but as soon as I call the URL it stops without generating an error.
This is my Service class:
package com.some.package;
import org.boris.winrun4j.AbstractService;
import org.boris.winrun4j.ServiceException;
/**
* A basic service.
*/
public class StockService extends AbstractService {
private StockServer srv;
public int serviceMain(String[] args) throws ServiceException {
while (!shutdown) {
try {
Thread.sleep(5000);
} catch(InterruptedException e) {
}
if(srv == null) {
try {
srv = new StockServer();
srv.start();
} catch (Exception e) {
}
}
}
return 0;
}
}
I found out that the service didn't want to start if I started the Jetty server from the serviceMain class. I had to start a new thread. So StockServer extends Thread:
public class StockServer extends Thread {
private Server server;
public void run() {
if (server == null) {
try {
server = new Server(8080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/example");
StockServlet stockServlet = new StockServlet();
context.addServlet(new ServletHolder(stockServlet), "/stock/*");
server.setHandler(context);
server.setStopAtShutdown(true);
server.start();
server.join();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}
Since it runs perfectly fine as Java application I just don't know how to get this thing debugged. I hope one of you can point me in the right direction :).
I ended up using the Java Service Wrapper (JSW). This seemed a lot more complex but ended up to be quite easy. It also provides logging by default so I could easily fix the errors. The JSW had problems finding the correct JDK, since JSW is 32bit and I installed JDK1.7 64 bit (and 1.6 32bit). Installing JDK1.7 32bit fixed it. That might have been the problem with WinRun4j as well, but that is something I will never know :).
I made a windows service from a jar file using WinRun4J, so far it's very basic.
package org.boris.winrun4j.test;
import java.io.BufferedWriter;
import java.io.FileWriter;
import org.boris.winrun4j.Service;
import org.boris.winrun4j.ServiceException;
public class ServiceWrite implements Service
{
private volatile boolean shutdown = false;
public int serviceMain(String[] args) throws ServiceException {
int count = 0;
while (!shutdown) {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
}
try {
FileWriter fstream = new FileWriter("result.txt");
BufferedWriter out = new BufferedWriter(fstream);
out.write("Counts: " + count);
out.close();
} catch (Exception e){
}
count++;
}
return 0;
}
public int serviceRequest(int control) throws ServiceException {
switch (control) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
shutdown = true;
break;
}
return 0;
}
}
When the service is started it just keeps writing every couple of seconds to result.txt located in the root folder.. (Just for trying out WinRun4J)
Now my question is, can I do a method in the service jar, like this
public void write(String s){
//Write some string s to result.txt
}
And then invoke this method from a different java file on the system, i.e
java WriteToFile SomeString
Where WriteToFile is supposed to invoke write with some argument.
Is it possible? if so, how ?
The overall purpose of this is to have a service running where I can invoke methods via a GUI.
to "invoke methods via a GUI", you can't do it with WinRun4J.
in general rule, a Windows Service can't have a GUI for security reason (except for special cases).
However, there are other tools to create a windows service from a Java application, with which it will be possible to have a service with GUI and able to interact with the Desktop.