servlets and classpath - java

I apologize for the crummy subject line.
I started at a new company last week and was thrown into the lion's den immediately. We had a application on the verge of going into production... One of my guys had the app running and QA'ed so I didn't delve too much into the details of what he was doing.
We had this:
../legacy/ROOT/
..............index.jsp
..............WEB-INF/
.....................lib/
.........................(libs)
.....................classes/
............................x.class
There's a virtual host in conf/server.xml that defined the root as ROOT.
The new application seems to have started as a copy/paste of the legacy app. It has no entry in server.xml. It piggybacks, so to speak, on the legacy app even though it is wholly separate. The new application is rooted at legacy/ROOT/NewApp:
../legacy/ROOT/
..............index.jsp
..............WEB-INF/
.....................lib/
........................(libs)
.....................classes/
............................x.class
..............NewApp/
....................index.jsp
....................WEB-INF/
...........................lib/
..............................(libs)
...........................classes/
..................................x.class
To get to the legacy app, you browse to http://theUrl. To get to NewApp, you browse to http://theUrl/NewApp. That all works fine. Maybe.
We installed the new application on Saturday night. Monday morning, we find that while NewApp works great, our users can't log in to the legacy site. They get an application-generated 'bad username' error.
It occurs to me I don't know why NewApp works at all. I did not change web.xml. So I surmise that Tomcat finds a URL it likes by looking at the HOST definitions, then sees /NewApp in the URL and is happy to traverse into the legacy/ROOT/NewApp sub-directory where it finds an index.jsp and we're off to the races.
Now I get very confused. The legacy code has a WEB-INF folder, and NewApp/WEB-INF also exists. I don't know why Tomcat is doing anything with the NewApp/WEB-INF. Yet it is because NewApp works great. To make matters worse, the libs and and class files in both apps are nearly identical in name and content. The x.class file in NewApp is slightly different than the one in legacy - NewApp has a new login method. NewApp's x.class still has the old login method in it, however, though nothing in NewApp uses it.
I'm trying to determine how legacy's failure is related to our deployment. It must be - but it's hard to see how. We checked file dates. Nothing in legacy has been changed. I'm thinking it's a classpath issue somehow.
So, do I have one application here, or two? I thought it was the latter. But I'm not so sure. I'm so confused I can't even ask a good question :-/
The only other oddity was that Tomcat didn't explode the WAR file though the configuration looked like it should have. We had to do it by hand - and it worked.
This is what happens when your lead developer says, "3 months" and management says, "No, 1 month." It's a credit it works at all.

Wow! Definitely a face-palm moment for you. :-(
Here's my stab at understanding it, and of course I could be quite wrong: at runtime Tomcat doesn't care necessarily what is at the same level on the filesystem as the application root, so long as it finds what it thinks is a valid WAR in the root. And it would make sense Tomcat would check to see if a given folder structure in a URL matches a webapp before looking to see if it's a folder IN a webapp. So that would explain the "new" app.
As for the original app, if it doesn't reference the "NewApp" folder at all, then Tomcat would have no reason to even look in there, and if it did it would ignore the fact it looks like a webapp. It's just another folder of files. I suspect you can probably access any file in there directly by hand-constructing a URL to it. Handy. :-p (Edit: probably not, thinking about it again, Tomcat would likely just take any 'NewApp' reference to mean the new app, not a file in the old app.)
So... you have two webapps. I think Tomcat will keep them separate. But... it would give me the heebie-jeebies to think I had something like that in production...

I echo what Rodney has said. Tomcat looks for WAR files and treats them as an individual webapp (I think...). In your case, as Rodney says, from Tomcat's point of view all you have there is a webapp that happens to have a folder with some class files in it.
What I suspect is happening is that NewApp has the same packages/class names as legacy, and therefore one is overwriting the other (as Tomcat is probably picking up the NewApp WEB-INF folder - I don't quite understand how but there you go).
This isn't what you or your company wants to hear, but they should never have been bundled together as two separate apps. If NewApp depends on Legacy, then you could have JAR'd up the required code from Legacy and included it as a dependency for NewApp. But as you have it now, they should be separate, and I would say that Tomcat is getting pretty confused.
But maybe someone else has a way to solve it as it exists right now... but I'd be surprised to be honest.

Related

tapestry strange behaviour

Well, at least it seems as tapestry issue.
I'm getting an exception when trying to access a specific page:
org.apache.tapestry.PageNotFoundException:
Page 'ASpecificPage' not found in application namespace.
Seems like some pages are found, and some are not, I'm new to this project, and so far cannot say is there any difference between them.
The error still occurs, even when whole jboss-4 instance, along with the deployed application, is copied from my coworkers' computers and run, whereas they do not experience such problems. Supposedly there is no other application-specific configuration, and it's all contained within the server instance.
Any ideas? Even not Tapestry-related? I tried using same java version, with update number precision, but in vain. Like, where should I start to debug?
EDIT:
I'm new to tapestry, so if anything is missing or just inaccurate, just let me know. Some details:
the application is deployed as war, copied to deploy directory,
unexploded
there are other jars copied as well, not sure if they are relevant
when searching for ASpecificPage, it seems that there is only a java
class (i.e. ASpecificPage.java), and there is missing appropriate
.html file
when searched in code, I found occurences like:
clearPopUp(cycle);
ASpecificPage page=(ASpecificPage) cycle.getPage("ASpecificPage");
page.show(cycle, ASpecificPage.TAB_2);
Any other details needed?

How to inject compile classes in existing jar?

I need to patch a system in production however I have already change the main trunck in CVS. Since there are a lot of changes I do not want to revert the code to a previous version as there is only one class involve.
Could this be done ?
Stop the application.
Safely replace a edited *.class into an existing jar.
Re-start the application.
Many Thanks
Unless the jars are signed by someone else, it can be done. Jar files are just zip files after all, you unzip them, replace the file and zip them up again. If the jars are signed and you have the key, you have to sign the new jar again. (The jar command is the preferred way to do this.)
However it's very bad practice to do this. Trust me, the amount of work you save now will be lost a hundredfold if this way of deploying becomes a habit. Unless there's a real crisis (your application doesn't even run or crashes every ten minutes for example), you should never ever do this.
If you use CVS, you should always tag your releases, but even if you haven't done that, you can check out the trunk as it was on the release date, create a branch from there, apply the change to the branch and rebuild your application.
It takes about ten minutes (plus compilation time) and it's a safe and proper method. You can launch the patched application in your test environment, test that it works and only deploy it afterwards.
Dropping a pre-compiled class file in an existing application can break the code in subtle ways that don't become apparent until several days later. Recompiling and retesting the application takes this risk away, leaving just the much smaller risk associated with any code change.
Yes, you very well can. make sure your new class compiles fine and then update the jar. jar -u is what you're looking for.

java web application best practices

I'm trying to figure out the optimum way to develop and release a fairly simple web application, and I'm running into several problems. I'll outline the decisions I've made, because somewhere I've clearly gone off the rails.. Hugely grateful for any help!
I have what I think is a fairly simple web application. It contains a couple of jsps that reference a couple of java beans, and the usual static html, js, css and images.
Decision 1) I wanted to have a clear and clean release procedure, such that I could develop on my local machine and then release reliably to a production machine. I therefore made the decision to package the application into a war file (including all the static resources), to minimize the separate bits and pieces I would need to release. So far so good?
Decision 2) I wanted things on my local machine to be as similar as possible to the production environment. So in my html, for example, I may have a reference to a static file such as http://static.foo.com/file . To keep this code working seamlessly on dev and prod, I decided to put static.foo.com in my /etc/hosts when developing locally, so that all the urls work correctly without changing anything.
Decision 3) I decided to use eclipse and maven to give me a best practice environment for administering and building my project.
So I have a nice tight set up now, except that:
Every time I want to change anything in development, like one line in an html file, I have to rebuild the entire project and then wait for tomcat to load the war before I can see if it's what I wanted. So my questions are:
1) Is there a way to connect up eclipse and tomcat so that I don't have to rebuild the war each time? ie tomcat is looking straight at my actual workspace to serve up the static files?
2)I think I'm maybe making things harder by using /etc/hosts to reflect production urls - is there a better way that doesn't involve manually changing over urls (relative urls are fine of course, but where you have many subdomains, say one for static files and one for dynamic, you have to write out the full path, surely?)
3) Is this really best practice?? How do people set things up so that they balance the requirement for an automated, all-encompassing build process on the one hand, and the speed and flexibility to be able to develop javascript and html and css quickly, as quickly as if one just pointed apache at the directory and developed live? What do people find works?
Many thanks!
Edit: Thanks all for your great responses! If I could mark them all right, I would.. This has really helped me out. What I'm hearing is that best practice is to conserve the structure of the webapp in development, and run it in as close an environment to production as possible. Seems like the differences between people are the extent to which people are prepared to hot deploy resources into the servlet container, circumventing the build process for a little extra speed or convenience. That makes sense. Thanks again.
This is much like what I have to do at work, although we use ant (for now?). Also, while I use an IDE (or two), I refuse to have one as part of my build process, EVER. Somebody needs to be able to understand and tune your build.
Is there a way to connect up eclipse
and tomcat so that I don't have to
rebuild the war each time?
1) I think you're relying too much on your IDE. Usually I have an Ant build.xml that has a couple of tasks: one is "build war" the other is "update jsps." Building the war compiles all the code, packages it, deploys it to Tomcat and restarts everything. Updating the jsps doesn't restart the server, it's just a straight copy from my local files to Tomcat's deployed instance. No restart necessary since they're JSPs. Takes about half a second.
where you have many subdomains, say
one for static files and one for
dynamic, you have to write out the
full path, surely?
2) No way, Jose. So you mean any time the server name changes, you have to recompile your code? If you need to support dynamic URLs you might just want to bite the bullet and take a look at a framework to do the heavy lifting for you. I'm partial to Stripes (which supports dynamic URL rewriting out-of-the-box)... there are others.
To answer #1, I would suggest the following:
Spend some time learning maven to build your .war without eclipse. It's not that hard with the proper archetype. See here for more details: http://maven.apache.org/guides/mini/guide-webapp.html
Maven can generate eclipse projects either through mvn eclipse:eclipse or by using the m2 plugin
For deployment to your local machine and to production, use the maven cargo plugin. http://cargo.codehaus.org/Maven2+plugin and http://blank.jasonwhaley.com/2010/03/automated-deployment-with-cargo-drive.html
To answer question #2, there's nothing wrong with modifying your /etc/hosts file to mimic production. Just have a quick script that lets you add/remove those entries and flushes your dns cache. I do exactly that all of the time. (be sure to make your browser clear its cache frequently also through its relevant setting).
To answer question #3) yes this is how you should be doing things. Each build should result in a single deployable artifact that you can deploy to any of your environments in one step. You need to make sure you can do this without your IDE and use the IDE only as a tool to help you during the development phase.
Others have already answered you, I'll just comment on this (this too long for a comment btw so I make it an answer):
Every time I want to change anything
in development, like one line in an
html file, I have to rebuild the
entire project and then wait for
tomcat to load the war before I can
see if it's what I wanted.
If you change one line in an html file, there's no need to rebuild the entire project.
Note that I always rebuild the full .war and redeploy my .war but this takes less than two seconds (less than one second to rezip the .war [that's really what a .war is, a zipped file] and less than one second to redeploy it) because:
you don't need to recompile your entire project when you simply change one line in an html file
Same when you change one .java file: you can simply recompile that one file and re-war.
I wrote my own Ant build file from scratch (no Maven here) and I've got several targets. I can force a "clean build", that shall re-compile everything but typically I'm simply repackaging and redeploying the .war
You can check it for yourself: build a .war, unzip it in, say, directory dir1, then modify one .html (or one .java/.class file) and build a new .war and unzip that new .war in, say, dir2.
Then compare dir1 and dir2: now fix your build process so that you can create that second .war without needing to recompile everything.
Changing one .html, .java, .jsp, .css, .js / whatever file and redeploying a new .war should be a matter of seconds (less than two seconds if you didn't throw the kitchen sink in your Webapp).
Note that on the very same project, another developer here prefers to "hot deploy" / replace the files directly in the exploded webapp (I prefer to redeploy a .war everytime and because my complete repackage/redeploy takes less than two seconds I'm fine with it that way).
You don't need to reconstruct war file if your project is an Dynamic Web App in Eclipse and configured Tomcat server properly. Follow the below instructions:
1) Check out the below of how to configure tomcat server with eclipse:
http://greatwebguy.com/programming/eclipse/make-eclipse-and-tomcat-play-nice-together/
2) Use relative paths for your application but not absolute paths.
3) If you follow the above 2 steps properly then you have a best environment for development.
during development, you should configure eclipse and tomcat so that no rebuild/redeloy is required. just modify html/css/jsp etc, save, and refresh browser to see the result.
but before deploying to production site, you should do a clean full build and test it carefully.
domains: they should be in a config file; dev and prod should have different config files.

Weblogic application complains about some classes missing, how to debug?

I have a rather complex J2EE app I don't have any documentation for and I am trying to get it to run.
I have gotten the ant build script to compile a EAR file that contains a WAR file, but this application even though I get "successfully deployed" on weblogic console is still not working.
There are many required jars missing from the EAR file, hence I get errors about missing classes on the console log when I deploy the app. Sometimes even after I check a particular class is there I still get the error.
What is a best way to debug and get this application running?
Is there any shortcuts in J2EE/Java to "find all dependencies", apps anybody knows to analyze code and find dependencies or anything like that?
Is there any shortcuts in J2EE/Java to "find all dependencies", apps anybody knows to analyze code and find dependencies or anything like that?
Do you mean a compiler? I'm actually semi serious here (even if the compiler won't give you the name of a missing JAR). Indeed, if you are compiling that application successfully with Ant, then you likely have all dependencies required at compile time (you may need more of them at runtime but, well, you'll need to execute the code to identify them). Maybe you just need to add more of them in the EAR during the packaging. Or maybe you need to add more dependencies at the app server classpath level.
In both case, search engines like jarFinder.com or Docjar.com or Jarhoo.com might help you to identify missing JARs and to solve your ClassNotFoundException or NoClassDefFoundError.
You should actually give readers more details about the missing classes, I'm sure people will be able to give you some hints and point you in the right direction.
I know of no easy way to deal with this. If the application provides an ant task build the EAR then the result should be a self-contained deployable application EAR. It is possible that the EAR requires extra libraries to be added to some class path, without documentation it's really hard to know what.
This phrase is, I guess, at the heart of the problem: "Sometimes even after I check a particular class is there I still get the error." Exactly what do you mean?
You are getting a class not found error but you can see the class in the EAR file? If that's the case then things are really difficult, and may well be some kind of classloader issue. I don't know WebLogic at all, but in WebSphere when deploying an application you have a choice of whether to give precedence to JARs in the EAR or to the same JAR in WebSphere itself. Some Applications demand one or the other setting. If there's anything like that in WebLogic then this may be your problem.
Another possible problem is that the app may depend on infrastructure libraries (Eg. XML parsers) that are supplied by WebLogic, but expects a different version that is supplied with the WebLogic version you are using.
Without documentation that the app is supported on the version of WebLogic you are using, I fear you're fighting a long, hard (or even a losing) battle.
I'd assume you managed to get this application put together and compiled in an IDE, maybe Eclipse. Why not simply put all the Jars that you had available at build time, into the EAR?
If you're having to debug this by trial and error, you can look at the list of missing classes and figure out which jars they belong to by Googling for the class names, or maybe you can find them in your IDE too.
If you have a lot of trouble identifying some classes, post the class names (or the error messages) here and someone should be able to tell you where to find the Jar it belongs in.
If you are able to successfully build and deploy the app, but still getting the errors about missing classes, probably some of the jar classes are called through reflection. You may want to search for reflection API calls in the Project through your IDE.
These calls may be something like,
"Class cls = Class.forName(..); cls.newInstance()."
Sometimes even after I check a
particular class is there I still get
the error.
This may be a deployment issue. check the classpath. Some of the jars may be missing from the Classpath.
Is there any shortcuts in J2EE/Java to
"find all dependencies", apps anybody
knows to analyze code and find
dependencies or anything like that?
I'd start by having another go at getting documentation ... or help ... from the original developers, if you can find them. [IMO, people who develop / provide software without any documentation deserve to be bugged incessantly by people asking silly questions.] But I guess you've already tried that.
Then there are the jarfinder.com and other services as mentioned in another answer. (New to me!)
If that fails, I'd try doing a Google search on the FQNs. The chances are that if the missing classes are part of a commonly used library you will hit the Javadocs ... or a posting from someone else with a similar problem to yours.
If the "Google it" approach fails, use the clues in the package naming for the missing classes to try and find where they come from. If they follow the Sun recommendations, the names should map to a company or (real or pseudo-) organization that you can locate by a web search.

How can you force recompilation of jsps in JBoss 4.2?

I hit on this nasty behavior on JBoss 4.2 in QA, and I want to nip it in the bud before we go into production and find some other corner case.
A jsp calles a method that had the following signature:
public void methodName(String arg)
This was changed to:
public void methodName(String arg, Object... args)
A pre-existing JSP called this method via:
methodName("param");
On deployment of the modified code, JBoss did not recompile the JSP and this caused a crash in QA. Adding a silly comment to the jsp fixed the problem (JBoss recognized that the JSP changed and recompiled it).
Is there a setting on JBoss to force recompilation of JSPs on restart?
EDIT: To clarify some points in the answer, the setup is that the JSPs are part of a war which is part of an ear. The ear has all classes in it, in a jar.
Regarding the desire to pre-compile, if the system doesn't think that the jsp needs compilation, will pre-compile force recompilation? It doesn't seem so. The error here is not a compliation error, it is a method invocation error because of the "changed" (at the byte code level, not really at the code level) method signature.
Addendum: Note that we experienced in production recently that even with the accepted answer's flag set the JSPs did not recompile, even though the JSP did in fact change. Major bug there, but regardless, JBoss was shutdown normally. At this point it is getting to be an old version of JBoss, but if you are still using it, deleting the content of the work and tmp directories is the only way to be sure.
I'm not changing the accepted answer simply because it really gets to the point of what the question was looking for. JBoss bugs are kind of a separate issue.
If the JSPs are part of a WAR that is part of an EAR that is being deployed as a jar, then I'm not clear why your JSPs are not being recompiled. Don't the JSPs in the war file have newer timestamps than their JBoss-compiled class files from the last deploy? If not, couldn't you touch the JSPs as part of building the WAR/EAR before deploying. [I'm referring to using the Unix "touch" command, not manually touching each JSP file.]
Alternatively, the DeleteWorkDirOnContextDestroy setting in $JBOSS/server/default/deploy/jboss-web.deployer/META-INF/jboss-service.xml may be what you are looking for. It is false by default, but setting it to true may be what you need. I think this should delete the JSPs' class files on redeploy so that they get recreated upon first access of each JSP.
See https://jira.jboss.org/jira/browse/JBAS-3358 for more info.
I don't know of a setting, but deleting the generated Java class file in the work directory of your JBoss instance will cause the JSP to be recompiled the next time it is called.
You coudl alter the JBoss startup scripts to explicitly delete the "tmp" and/or "work" directories, where the compiled JSPs are stored. JBoss would then have no choice but to recompile them all.
Not subtle, but it would do the job.
One option for you would be to precompile all of your jsp's at build time. This would quickly flag any compilation errors.
You could also do this in production - speeding up first access but I get the feeling you want this more for a QA step than anything else. If so, you could add the precompile step to the your testing phase in your build tool of choice - and so to your CI environment. This would provide assurance that jsp's that don't compile won't make it out of test.
See this for details on running a precompile task:
Jboss Jasper configuration
Hope this helps.
Some JSP containers (as per section 8.4.2 of the JSP 1.2 specification) support the capability of precompiling a JSP page.
To precompile a JSP page, access the page with a query string of ?jsp_precompile
http://hostname.com/mywebapp/mypage.jsp?jsp_precompile
The JSP page will not be executed. If the container supports precompilation, the JSP page will be compiled if necessary.
See also http://www.rgagnon.com/javadetails/java-0414.html
Pablojim is on the right track. You just need some more info to get a complete view of what's going on. Here's how I understand it.
In prod, you've changed a jsp that requires other jsps to be recompiled. In order for them to be recompiled one of 2 things must happen
The compiled version of the jsp needs to be deleted.
The jsp itself needs to be modified (or even if it's "touched" - modified date is updated)
If you still need to verify that all your jsps work, they will all need to be precompiled using an ant task. this also allows you to deploy the war file with the precompiled jsps in the war file. This should solve your problem.
If your files are not deployed in a war file, but in an exploded format, you should seriously consider packaging your web app in a war file for deployment. This makes it a nice package to deploy between environments.

Categories

Resources