How to monitor file changes within a Java EE application? - java

Is there a portable way of monitoring a file for changes from a deployed application?
As a practical example, suppose that I have an application deployed in a Glassfish AS. Then, as the application runs, I deploy a configuration file inside /META-INF, which will be read by some Thread from my application.
So, how to implement this? How to monitor a file (or a directory, for that matter) for changes?

Java7 has a platform independent solution to monitor files in the filesystem:
http://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html
Here is a short tutorial on how to use it:
http://docs.oracle.com/javase/tutorial/essential/io/notification.html
But as jtahlborn has correctly said, if the container does not explode the .war file, there is nothing to watch.

I think it would be impossible to provide such a mechanism. You are assuming "/META-INF" is on the file system, but i don't believe there is any requirement for a Java EE application to be exploded to the filesystem. (yes, many app servers do that for a variety of good reasons, but i'm pretty sure that is not a requirement).

Classloaders cache heavily and a resource when first read is not updated anymore from the underlying filesystem.
Regardless, you are not allowed to access and manipulate the file system in pure Java EE. The reason is simple - if your thread is migrated to another node (for clustered serveres) your files will not be available anymore. The container must know about these things so it can handle them properly, and filesystems are not one of "these things".

Please check the answer from #jtahlborn. If you do have access to the filesystem, then you have two choices:
The portable inefficient way is to periodically poll the filesystem and check for changes, either by checking the modification date or CRCins the file (slower)
The efficient way is to use the native OS functionalities to be notified when the file changes. Check out libnotify. It's going to require local native libraries, however.

Related

EJB 1.1 and disk io

I have a question about EJB 1.1 (yes really that old ... - please dont kid me...)
So the question is one of the old ones: Why should I not do disk io within EJBs? Especially reading files.
To be more precise the use case: Its all about one file that is needed as a template for some special data export.
So it is:
one file
very rarely changed (for example within a special maintanance time)
rarely read
no heavy load
Is there any reason, why not to read that template file from disk?
Are there any technical restrictions like an ForbiddenOperationException when I try to do disk io within an EJB. I already run a test and reading and transferring is working just fine. Is that behaviour different within EJB 2.x or 3.x?
Thanks a lot!
This restriction is in the specification to allow for clustering of EJBs, which is easier if the EJB is self-contained and does not rely on the outside environment such as the file system.
Accessing the file system should work fine however, if you truly want to comply with the spec you could bundle the file inside the EJB jar and access it from the classpath using Class.getResourceAsStream.
From EJB Restrictions :
Enterprise beans aren't allowed to access files primarily because
files are not transactional resources. Allowing EJBs to access files
or directories in the filesystem, or to use file descriptors, would
compromise component distributability, and would be a security hazard.
You can pack file in a JAR so that you can read it with getResource() or getResourceAsStream() & can change, redeploy independently.
Else, if you haven't encountered any issues with your current approach, then its fine, but isn't recommended in accordance with the specification.
Why not store it in database as a blob?
then you can have an admin webapp to change it from browser
and you can use ldap to authenticate/authorize the webapp
oops, sorry for making life so complicated

Disk storage in a Java EE application

I have Java EE app in which I want to small little amount of data to disk, eg just user/passwords.
I dont want to go through the hassle of integrating with a full db for this little amount of data.
Is there a standard way to access the file system and a standard folder where web applications can store their data on disk, other than using a database?
Note:
I am not using EJBs. It's a web application using Servlets.
You could consider using the preferences API to store this data - it's available on Java EE as well.
Use a simple Java based database, like HSqlDB or h2. The setup won't be that complicated compared to a heavyweight DB. The key advantage this will give you is managing concurrent updates, which you would have to code yourself if you use direct file access.
File access has always been a controversial activity within EJB-based applications because of the restrictions placed upon bean providers by the EJB specification. The part of the specification relevant here is under the section entitled Programming Restrictions, and it states the following about accessing the filing system.
An enterprise bean must not use the
java.io package to attempt to access
files and directories in the file
system.
This is a fairly specific statement, and is followed up by a short explanation of why this is the case.
The file system APIs are not
well-suited for business components to
access data. Business components
should use a resource manager API,
such as JDBC, to store data.
While this explanation highlights a key reason for not using file I/O, I think that there is much more to this. However, although this is a well known restriction, actually finding more information on this is a time consuming task. So, in the quest for knowledge, I did some digging and came up with the following reasons why file I/O is "a bad thing"TM.
The WORA mantra of Java and J2EE means that there might not actually be a filing system to access. I've seen various comments saying that the J2EE server might be running on some device that doesn't have a filing system, or the application server doesn't have access to the filing because it's deployed in, for example, a database server. Although this is a valid reason, I don't think that this applies to most projects.
Access to files isn't transactional. Yes, typically, files aren't transational resources and when building enterprise systems, you usually want to be sure that some information has been correctly and accurately stored, hence the use of relational databases and the like.
Accessing file systems is a potential security hole. If we look at how other resources (e.g. JDBC DataSources, JMS Topics, etc) are accessed, it's usually through JNDI. To ensure that only authorised parties can access these, we typically have such resources protected by some sort of authentication mechanism, be it a username/password combination, or an SSL certificate. The problem with filing systems is that they are much more open and it's harder to control access. One solution is to lock file access via the operating system, and another is to use the Java security model to restrict access to only a specific part of the disk. If you are going to access the filing system from your business components, then locking down access will help to make the system more secure and resilient to attacks.
So then, how are we supposed to access files from EJB? Many people advocate the use of an intermediary Java class to wrap up the file access, believing that the EJB specification only disallows access from the bean class itself. Is this true? I'm not convined because all the same reasons apply. The specification itself presents an answer, and that answer is to use a resource manager so that we can treat file access as a secure, transactional, pooled resource. One such implementation is a J2EE Connector Architecture (JCA) adapter that you write, deploy and configure to access your filing system. In fact, some vendors have already built JCA adapters that access flat files and these are particularly useful if you have to access the outputs of legacy, mainframe systems.
Of course, many types of file access can be worked around. For example, configuration information can be placed in LDAP, JNDI, a database, or even properties files delivered inside your JAR files that get loaded as a resource through the classloader. In those circumstances where accessing files is a requirement, then other solutions include loading the file through the servlet container, having it sent to the EJB tier via messaging, downloading the file from a webserver through a socket connection and so on.
These are all workarounds for the programming restriction but at the end of the day I think you have to be pragmatic. Many projects do utilise file access from within the EJB tier and their solutions work. Although the EJB specification imposes a restriction, in reality many vendors choose not to enforce this, meaning that using the java.io package for accessing files is possible. Whatever solution you come up with, you should ideally keep the specification in mind. It's there to help you build portable and upgradable applications, but pragmatism should be employed. Hopefully a future version of the EJB specification will address this issue in more detail and this controversy will become a thing of the past.
Credit for above goes to: Simon Brown
In addition it is never a good idea to keep passwords on your site. Most security references tell you it is OK to keep a hash of a password that you can check against. If somebody breaks into your site, then he could retrieve all the passwords for all users.
Is there a standard way to access the file system and a standard folder where web applications can store their data on disk, other than using a database?
There's the java.io.File API, but the Servlet or the Java EE specifications do not provide a standard directory where you may persist files for a lengthy duration. At the very best, the javax.servlet.context.tempdir attribute may be used to locate the location of the temporary files directory, from the ServletContext.
The rationale for not supporting standard file directories is due to the inability to predict whether the container would be able to access a file system in the first place. Your container might be running off an embedded device in the first place to begin with, that might rely on a SAN or another remote file system for persisting files.
In addition, using the File API in EJBs is frowned upon, due to the non-transactional nature of file systems, so there is no similar concept of working directories and files.

Storing and editing configuration for Java EE applications

UPDATE: See my blog post on this topic about a year after this was written: http://blog.ringerc.id.au/2012/07/java-ee-7-needs-improvements-in-app.html
... for references to the Java EE 7 planning discussion on this topic.
I've mostly finished writing a small Java EE 6 application, and am in the process of replacing the hard-coded preferences with a proper dynamic configuration interface.
I'm not sure how - or, more specifically, where - to store settings. There must be some obvious, "standard" way to do this that's expected to "just work" across various frameworks and containers, but for the life of me I cannot find it.
What I want is a simple way to load and store settings, one that works across different app servers and OSes, doesn't require any confguration by the user, and actually works properly. The Java Preferences API would be ideal - but seems broken under Glassfish 3.1.
Options for storing configuration would theoretically include:
Using context-parameters from the container environment
Storing them using the Java Preferences API
Reading/writing a properties file ... somewhere
Using JPA to store them in a JavaDB provided by the container
Putting it in a properties file that's loaded off the classpath
Using system properties to set configuration options, or path to a .properties file
This would seem to be a basic requirement that'd be well catered for in an environment where the container supposedly provides you with all the core services you might need - but all these approaches have issues.
A bug in glassfish renders (1) unworkable, and in any case the Glassfish web admin user interface lacks any way to configure context parameters, so you have to use `asadmin' and some less than lovely command line syntax to do it. Context parameters can only be accessed via the ServletContext - which isn't accessible in a consistent way between frameworks like JSF2, JAX-RS, and raw servlets - but at least Seam Servlet handles that.
What appears to be another bug in glassfish was a library version conflict between the deployed app and Glassfish breaks (2). The preferences backend fails to flush preferences to disk, so the stored preferences data is lost when the application server is restarted. The Java Preferences API also seems to be considered a J2SE/desktop thing, despite its inclusion in the Java EE 6 specs.
(3) might work - but there's no way to know where your app has read/write access on the file system and where it should look. You can't make this configurable, as it becomes a chicken-and-egg problem then. Various platform-specific guesses could be applied, but would break in the presence of a SecurityManager.
(4) would work, but it's nuking a fly. It requires that a JavaDB service be running and forces the user to make sure the JDBC and pool resources in the app server are configured properly. It's big and complicated for a simple job, and entity modelling isn't a lovely fit for preferences storage anyway, as it'll mostly land up being key/value structured.
(5) would work, but requires users to know where to put the config file where it'll be found under various different app servers. It also makes it hard for the app to provide any kind of configuration UI because it can't necessarily find the local path to the config file or open it for writing, especially in the presence of a SecurityManager.
(6) would also work, but forces the user to configure the configuration system before they can configure the application. Needless to say, that doesn't excite me, given how relatively complicated deploying the app and creating the resources already is for users who don't already know Glassfish/EE.
So ... how are you handling configuration and storage of options? Have you found a way that lets you "just do it" without the user having to configure anything to allow your app to store its configuration?
The problem with the preferences API was caused by the inclusion of jaxb and stax implementation jars on in the application's war, pulled in by jersey-json . With these excluded (as they're provided by the app server anyway) the preferences API resumed functioning correctly.
It looks like the prefs API with custom UI for setup appears to be the best way to go.
Though not the environment you spoke about: http://www.osgi.org/javadoc/r4v42/org/osgi/service/cm/ConfigurationAdmin.html

Get current EAR path with WebLogic

In my source code, I'd like to get programmatically, the last modified date of the current EAR from which my code is deployed.
I'm using Oracle WebLogic.
How could I do that?
Thx for your answers
I'd suggest stepping back and looking at the problem you're trying to solve, Eric.
Do you want to know when the application was built or the particular version of the application you've got deployed? If that's the case, you're probably best served by incorporating something into the build process to set this. Ideally a manifest of the specific component versions used to package up your application.
If you want to know when the application was first deployed by an administrator, or most recently deployed that gets more tricky. Relying on the filesystem to solve this problem is a bad idea because you're at the mercy of whatever WebLogic Server is doing, which is admittedly more than a bit opaque.
If you absolutely need to do this, WebLogic Server's standard staging behaviour puts a version of the file in a particular subdirectory on each server instance, then very quickly pulls it apart. (it's the 'servers//stage' subdirectory underneath the root directory of the domain ($DOMAIN_HOME) $DOMAIN_HOME is the current directory for all server processes at runtime, so the relative path should work fine.
That should give you the time that file was deployed across the network, but you'd definitely want to test the observed behaviour from rebooting your server instance.
The problem with that is that it doesn't give you anything you couldn't determine more elegantly via either the build process, or WLST scripting around the deployment process.
If it's the last time the application itself was deployed (regardless of the version) then application lifecycle event listeners are definitely the best way to go. Unfortunately there's no MBean that gives you the uptime of an individual application.
There's a great reference on lifecycle listeners here:
http://download.oracle.com/docs/cd/E17904_01/web.1111/e13712/app_events.htm#i178290
You could either check the file properties or see inside the MANIFEST.MF present inside the EAR.

FileIO from an stateless session bean

I need to read and write xml files to disk from a stateless session bean running in JBoss. What is the preferred way of doing this?
Right now we are accessing the file system using the java.io classes.
I found a simple file system RAR example and have got that working but it may be more buggy than just using java.io directly.
If you check that Programming Restrictions section of the EJB Spec you'll find the following:
An enterprise bean must not use the java.io package to attempt to
access files and directories in the
file system.
If you can find a better, possibly secure and more importantly, transactional way of doing it, please reconsider. We have a system that stores PDF documents as blobs in a database and then serves them to the users by email or servlet.
The JBoss JCA based FSManagedConnectionFactory isn't too bad. It is JNDI based and likely to work in more instances than just hacking around java.io
If you properly close the file and clean up, you can use anything you want.
I would use an XML parser to read or write XML files, though, it is safer.
The complicated way is to write a ejb client that reads the file, or set up an xml datasource somehow. In reality, nothing bad will happen if you use java.io in the session bean. However, if you are using clustering and/or will migrate the servers, then you have to take care of where your beans are running and which one will be invoked.
The simplest "batch" solutions is to take one machine out of the cluster and run the "batch" applications there.

Categories