GlassFish: email log handler - java

We need to send mail from a log handler in GlassFish v3.1.2.2.
We've tried to use smtphandler-0.6 and -0.7 with limited success. We install the jarfile to domain/lib/ext, and configure smtphandler's properties in domain/config/logging.properties. We've tried two ways of satisfying smtphandler's reliance on mail imports: 1) Editing its manifest classpath to point to ../../../../modules/javax.mail.jar, and 2) Putting javax.mail.jar in domain/lib/ext alongside the smtphandler jar. (We prefer the former approach so that the same javamail classes are used throughout the system. But it seems to make no difference, no worky either way.)
With either of these arrangements the behavior is the same:
The handler loads ok as shown by the JVM's verbose:class output.
Sometimes it sends mail for errors and warnings that occur during domain startup (like the expired certificate). Other times it fails as described below before ever sending mail.
It always fails once we've deployed our application and logged some application errors. We can tell that the handler is invoked - that's evidenced by the debugger and some primitive System.out "logging".
The root problem is a NoSuchMethodException: com.sun.mail.smtp.SMTPTransport.[init](Session, URLName). The failure to find the c'tor means the transport object can't be created; that eventually manifests as a NoSuchProtocolException.
We can see that the SMTPTransport class is loaded and that it has the requested c'tor. Our best theory is that class loading is somehow involved, but we've not been able to figure out exactly how. (Yet. We're working that angle now.)
Questions:
Are we deploying the handler to the correct location (domain/lib/ext) ?
Why does it (sometimes) send mail during an (empty, no apps) domain startup, but fail for logs emitted by our application? That's a race condition, surely, but what are those threads doing that occasionally works for a bit then reliably fails?
Are we right to think that all these indications together imply a class loading issue?
We've reproduced these symptoms on GlassFish versions 3.1.2.2 and 4, with JavaMail 1.4.4 and 1.5.
Thanks in advance for any help.

Looks like a bug was reported for this NoSuchMethodException when attempting to install log handler in GlassFish server. This issue was raised with the JavaMail team under Bug K6668 | GH144 - skip unusable Store and Transport classes which is fixed in JavaMail 1.5.3. Upgrading the JavaMail module in glassfish and all other copies deployed should correct the issue. It is also helpful to start glassfish with "glassfish/bin/asadmin start-domain -v" so you can see all bootstrapping messages.
•Are we deploying the handler to the correct location (domain/lib/ext)
Yes. You can deploy to domain/lib/ext but you have to include JavaMail 1.5.3 so you find the correct transport service. Otherwise, you can deploy the jar to the modules dir and add the HK2 metadata to make the smtphandler look like a service. This can be done by including new jar or modifying the existing smtphandler jar.
For GlassFish 3, the file META-INF/inhabitants/default must be added containing the following:
class=smtphandler.SMTPHandler,index=java.util.logging.Handler
For GlassFish 4, the file META-INF/hk2-locator/default must be added containing the following:
[smtphandler.SMTPHandler]
contract={java.util.logging.Handler}
scope=javax.inject.Singleton
The jar then has to be placed in the glassfish/modules folder along with upgrading javax.mail.jar.
Another option is to subclass smtphandler to look like a HK2 log handler service and add a preDestroy method to trigger the email on shutdown. This is described in the Oracle GlassFish Server 3.1 Administration Guide Part I section 7 titled Adding a Custom Logging Handler.
Under GlassFish 4 there are two different ways to locate a handler in the logging.properties. Handlers placed in domain/lib/ext use the standard handlers key in the properties file. Handlers placed in the glassfish/modules as a HK2 Service are loaded using the handlerServices key in the properties file.
#GF3 ext/endorsed or OSGI. GF4 ext/endorsed only.
handlers=smtphandler.SMTPHandler
#GF4 OSGI only, 'handlerServices' should not contain any whitespace characters between handlers.
handlerServices=com.sun.enterprise.server.logging.GFFileHandler,smtphandler.SMTPHandler
•Why does it (sometimes) send mail during an (empty, no apps) domain startup, but fail for logs emitted by our application? That's a race condition, surely, but what are those threads doing that occasionally works for a bit then reliably fails?
Difference in context class loader between messages logged by GF vs your web app. The CCL is used in JavaMail to locate the transport. Patching the 'sendBuffer' method should fix the behavior:
#Override
protected void sendBuffer() {
final Thread thread = Thread.currentThread();
ClassLoader ccl = null;
try {
ccl = thread.getContextClassLoader();
thread.setContextClassLoader(
javax.mail.Transport.class.getClassLoader());
} catch (SecurityException ignore) {
}
try {
super.sendBuffer();
} finally {
try {
thread.getContextClassLoader();
thread.setContextClassLoader(ccl);
} catch (SecurityException ignore) {
}
}
}
•Are we right to think that all these indications together imply a class loading issue?
Yes. The only way to really make this work right is to subclass or patch the smtphandler to look like an HK2 component and modify the CCL.
We need to send mail from a log handler in GlassFish v3.1.2.2.
We've tried to use smtphandler-0.6 and -0.7 with limited success.
Disclaimer: I'm a content developer for MailHandler included with JavaMail project.
An alternative to the smtphandler is the com.sun.mail.util.logging.MailHandler included with the JavaMail reference implementation.
Under GlassFish 4 you have to have glassfish/modules/javax.mail.jar that is JavaMail 1.5.3 or newer. An updated version can be downloaded from the JavaMail API homepage and can be used to replace the version bundled with GlassFish.
Next you have to modify the logging.properties for the domain. Here is a sample configuration you can include to get you started.
#Ensure no whitespace between handler class names.
handlerServices=com.sun.enterprise.server.logging.GFFileHandler,com.sun.mail.util.logging.MailHandler
com.sun.mail.util.logging.MailHandler.subject=com.sun.mail.util.logging.CollectorFormatter
#com.sun.mail.util.logging.CollectorFormatter.format=GlassFish 4.x:{0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer} more}
#com.sun.mail.util.logging.CompactFormatter.format=[%4$-7.7s] %7$#.140s
com.sun.mail.util.logging.MailHandler.level=WARNING
com.sun.mail.util.logging.MailHandler.filter=com.sun.mail.util.logging.DurationFilter
com.sun.mail.util.logging.MailHandler.pushLevel=WARNING
com.sun.mail.util.logging.MailHandler.mail.smtp.host=some-smtp-host
#com.sun.mail.util.logging.MailHandler.mail.user=some-user
#com.sun.mail.util.logging.MailHandler.authenticator=some-password
com.sun.mail.util.logging.MailHandler.mail.from=app#server.com
#com.sun.mail.util.logging.MailHandler.mail.sender=team#list.com
com.sun.mail.util.logging.MailHandler.mail.to=devs#bugfixers.com
com.sun.mail.util.logging.MailHandler.verify=resolve
com.sun.mail.util.logging.MailHandler.mail.smtp.quitwait=false
com.sun.mail.util.logging.MailHandler.mail.smtps.quitwait=false
com.sun.mail.util.logging.MailHandler.mail.smtp.connectiontimeout=45000
com.sun.mail.util.logging.MailHandler.mail.smtps.connectiontimeout=45000
com.sun.mail.util.logging.MailHandler.mail.smtp.timeout=45000
com.sun.mail.util.logging.MailHandler.mail.smtps.timeout=45000
For GlassFish 3 you have to install JavaMail (javax.mail.jar) under the domain/lib/ext or glassfish/lib/endorsed and test that this doesn't break any of your applications. This classloader configuration also works under GlassFish 4 and allows combining the MailHandler with MemoryHandler which can emulate the behavior of the smtphandler.
Next you have to modify the logging.properties for the domain. You can use the same sample as the GlassFish 4 except that you have to use the standard handlers tag instead of the handlerServices.
handlers=java.util.logging.MemoryHandler
java.util.logging.MemoryHandler.target=com.sun.mail.util.logging.MailHandler
java.util.logging.MemoryHandler.size=512
java.util.logging.MemoryHandler.level=INFO
java.util.logging.MemoryHandler.push=WARNING
com.sun.mail.util.logging.MailHandler.capacity=512
com.sun.mail.util.logging.MailHandler.level=INFO
com.sun.mail.util.logging.MailHandler.pushLevel=WARNING
com.sun.mail.util.logging.MailHandler.filter=com.sun.mail.util.logging.DurationFilter
com.sun.mail.util.logging.DurationFilter.records=512
com.sun.mail.util.logging.DurationFilter.duration=5*60*1000

Related

Use log4j on a deployed war

I've made a trivial RESTful service on a JBoss server that just says "hello" when it receives any request. This part works and it is already deployed.
To go further with my project I need to log. The other applications on the server use log4j configured by a log4j.xml placed on a specific folder on the server.
For an offline project I'm used to have a runnable main method that in this case I would use to execute DOMConfigurator.configure(filepath) (where filepath is the log4j.xml) and I will be expecting to log with those settings.
However, since this is a deployed service (and since I'm not skilled enough to figure it myself) how would I so such a thing?
The question in my opinion could be interpreted in two ways:
How do I run some code "for sure" when I deploy something (in similar way to a main method) ? Do i need something like spring-boot?
How do I setup log4j on a deployed service on JBoss?
(I don't know if one question excludes the other...)
Thanks for the help!
1) If you want to run some code "for sure" you can create #PostConstruct method in one of your beans. Both Spring and EJB support it.
2)As for log4J configuration it should be enough to put it in classpath and add corresponding dependencies, no explicit configuration of path should be needed.

Jboss wildfly 10 strange issue loading libraries

I'm developing a custom application for IBM BPM that uses these libraries: Jace.jar, pe.jar, log4j.jar, stax-api.jar, xlxpScanner.jar and xlxpScannerUtils.jar that are used to call a web service.
When I create a java project and add those libraries, it works fine. It also works ok when I create a dynamic web project using tomcat 8 as server; but when using jboss I don't get the expected result when calling the web service. So, does anybody know how to disable the modules that use these libraries? Or where to find information about it?
I want my application to be server independent.
This is my code:
try {
VWSession vwSession = new VWSession("userName", "userPass", "connPt");
vwSession.isLoggedOn(); /* It's loaded with 'false' value in jboss.
In tomcat it's loaded with 'true' value */
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.FATAL, "Details: ", e);
}
To create an application that is server agnostic, you need to strictly adhere to Java EE specification. Meaning, use only those jars that are bundled as part of the Java EE version you are using. Then too, there are certain deployment descriptors specific to a given application server that would need to be used in some cases. For example - jboss-deployment-structure.xml, ibm-application-bnd.xml, etc.
In your case, xlxpScanner.jar is not a part of the Java EE spec, so making the application server independent is not possible with the current settings. You could look for a replacement of the part of this jar you are using with something Java EE has. In short, get rid of this jar alongwith xlxpScannerUtils.jar.
Alternatively, if you want jboss to run the application properly, add all the jars in a module and give it to the EAR/WAR using jboss-deployment-structure.xml. Details can be found here.

Configuring cxf service listing in karaf

It is a web service deployed on Apache Karaf using camel-cxf. I am able to see the cxf service listing in URL localhost:8181/cxf which has some rest and soap services deployed on it.
The problem is it is returning the service listing whenever any request comes with keyword "services". For example the url http://localhost:8181/abcd/services returns cxf service listing page instead of processing the actual request.
I got to know from http://cxf.apache.org/docs/jaxrs-services-description.html that its is because of the default value of service-list-path of CXFServet is services.
Here is my Question. If I want to override this, I should set this property in etc/org.apache.cxf.osgi.cfg. This cfg file is not present under etc folder in my karaf. What are the steps to be taken if I am creating this property file manually? What features I need to install? Or creating this cfg is sufficient ?
Appreciate your help !
There should be no extra installation requirements, just create a new file etc/org.apache.cxf.osgi.cfg.
There are three settings you may be interested in:
org.apache.cxf.servlet.context = /mycxf
org.apache.cxf.servlet.service-list-path = /myservices
org.apache.cxf.servlet.hide-service-list-page = false
Where the default URL for the CXF service listing is usually like http://localhost:8181/cxf/services, with the changes above the URL would become http://localhost:8181/mycxf/myservices
If you change from false (default value) to true, then your services will be hidden and you will instead get a page stating No service was found.
Because these are initialisation settings you need to shut down Karaf for the changes to apply.
I see several points here --
The CXF framework is installed by default in karaf under the context-path /cxf.
/cxf/services can be considered as a CXF internal app that displays the list of services deployed in CXF. I don't think you can configure the name "services" here (and why would you change that?)
the "url-pattern in web.xml" you speak of (if I understand correctly) determines the context path of your servlet/application. You can specify this is camel like this:
<cxf:rsServer id="secureRsServer" address="https://0.0.0.0:8182/my/path/"
serviceClass="....">
(for the RS Server, probably same for the WS server).

Toplink runtime error in Tomcat 6

Let me give a little background to give context to this question. Over the course of time, the application I have been assigned to work on lost the ability to be built and deployed as a full application. By that I mean the previous developer compiled the code in his local IDE, and dropped single class files, as opposed to building out proper JARs and WARs to be deployed out to tomcat. So I have been tasked to clean up the project so it is a standard deployable app again. So to sum up the important part, the application exists in a working format on a windows tomcat environment, that hasn't had a clean deploy in a long time, my goal is to make the app buildable and deployable via a jenkins CI server to a tomcat instance running on a Linux server. Now on to the problem. I get the following toplink exception in one application module.
Local Exception Stack:
Exception [TOPLINK-6007] (OracleAS TopLink - 10g (9.0.4) (Build 031126)): oracle.toplink.exceptions.QueryException
Exception Description: Missing descriptor for [class edu.cornell.finsys.datamodel.AccountDTO].
Query: ReadObjectQuery(edu.cornell.finsys.datamodel.AccountDTO)
I have verified that the toplink mapping file has been loaded by tomcat, and the AccountDTO is mapped in the file. To double check that, I have moved the mapping file out, and I get a completly different error at load time. I know the file mapping is correct, as it worked correctly on the old server. I don't know what else could be causing the toplink exception.
For more information, I am on tomcat version 6.0.37, Java version 1.6.0_45, toplink version 9.0.4.031126
Any ideas?
This may be it; right from Metalink:
Cause:
This is an issue with loading the sessions.xml file, once when the application was first deployed using that application's classloader and then not reloading the session when the application was redeployed and a new XMLContext was created. The caused us to attempt to use a session that had been loaded with a different classloader than the current application.
Possible solution:
The issue occurs when TopLink is part of the main classpath (Eg: Applib directory rather than stored in the ear file). When this happens, the static SessionManager has trouble finding descriptors the second and subsequent times an application is deployed. Basically the descriptors are indexed by class. When the application is redeployed, the classes used for the indexing are unloaded and new versions of those classes appear. This causes "Descriptor not found" exceptions.
Potential Workarounds
Put the toplink.jar file in the ear.
Write some code to manually clean up when the toplink application is torn down.
For certain architectures they may be able to use the version sessionManager.getSession() that allows the session to be refreshed.
Workaround 3 is best and very feasible. The session has to be lazily
obtained from the SessionManager and held in an application code
variable. When the application is restarted its handle on the TopLink
session will be null. The session can then be obtained from the
SesssionManager using the getSession(XMLLoader xmlLoader, String
sessionName, ClassLoader objectClassLoader, boolean
shouldLoginSession, boolean shouldRefreshSession) API with
shouldRefreshSession set to true. This will ensure that the old
session (if any) is logged out and removed from the SessionManager
before a new one is created.
Did any of the workarounds help?

Filter log4j 2.0 messages to separate log files per-webapp

Executive Summary
How do I filter by the servlet in which the log message was invoked? (presently using 2.0 beta8)
Why on earth I would want to do that...
I have several existing web applications. They were written to rely on a proprietary logging system. I have re-implemented a key class from the proprietary system from scratch and added it as a class the proprietary system as a jar and log4j 2.0 as jars in tomcat, thereby utilizing the class loading load order in tomcat to divert the proprietary system into log4j. This succeeds and my log4j config now controls everything (Yay!).
But... (There's always a "But"!)
I was very pleased until I discovered that with all 4 applications deployed in the same container, they were not coordinating their writes to the single log file in the single configuration I had placed in conf/log4j2.xml (and specifed by passing -Dlog4j.configurationFile=/mnt/kui/tomcat/conf/log4j2.xml on the command line). I found some log messages with much earlier time stamps (hours earlier) in the middle of the log file. Out of order logs (and possibly overwritten log lines?) are not desirable of course.
I actually don't want them all in one file anyway and would prefer a log per application controlled by a single config file. Initially I thought this would be easy to achieve since log4j automatically sets up a LoggingContext with the name of the web application.
However I can't seem to find a filter implementation that will allow me to filter on the LoggingContext. I understand that from each application's perspective there is only one logging context (I think), but the same config file is read by 4 applications so from the config perspective LoggingContext is not unique.
I'm looking for a way to route each application to it's own file without having a config file for every application, or having to add classes to all the applications or edit war files (including web.xml). I'm sooo... close but It's not working.
Just to complicate matters, there is a jar file we wrote that is shared among all 4 applications that uses this logging too and one application has converted to using log4j directly in it's classes (but it still uses proprietary classes that reference the proprietary logging class that I replaced).
I have already seen http://logging.apache.org/log4j/2.x/manual/logsep.html and my case seems closest to '"Shared" Web Applications and REST Service Containers' but that case doesn't seem very well covered by that page.
You may want to look at the RoutingAppender which can be used to separate log files based on data in your ThreadContextMap. You could use the web app name as a unique key.
About the out of order logs, there was an issue with FastFileAppender in older betas. If append was false, the old file was not truncated but new log events would start to overwrite the old file from the beginning. (So after your most recent log event you would see yesterday's log events, for example). What version are you using?

Categories