Posts Tagged ‘Java’
[DevoxxFR2012] Android Development Essentials: A Comprehensive Introduction to Core Concepts and Best Practices
Lecturer
Mathias Seguy founded Android2EE, specializing in Android training, expertise, and consulting. Holding a PhD in Fundamental Mathematics and an engineering degree from ENSEEIHT, he transitioned from critical J2EE projects—serving as technical expert, manager, project leader, and technical director—to focus on Android. Mathias authored multiple books on Android development, available via Android2ee.com, and contributes articles to Developpez.com.
Abstract
This article examines Mathias Seguy’s introductory session on Android development, designed to equip Java programmers with foundational knowledge for building mobile applications. It explores the Android ecosystem’s global context, core components like activities, intents, and services, and practical implementation strategies. Situated within the rapid evolution of mobile IT, the analysis reviews methodologies for UI construction, resource management, asynchronous processing, and data handling. Through code examples and architectural patterns, it assesses implications for application lifecycle management, performance optimization, and testing, providing a roadmap for novices to navigate Android’s intricacies effectively.
Positioning Android Within the Global IT Landscape
Android’s prominence in mobile computing stems from its open-source roots and widespread adoption. Mathias begins by contextualizing Android in the IT world, noting its Linux-based kernel enhanced with Java libraries for application development. This hybrid architecture leverages Java’s familiarity while optimizing for mobile constraints like battery life and varying screen sizes.
The ecosystem encompasses devices from smartphones to tablets, supported by Google’s Play Store for distribution. Key players include manufacturers (e.g., Samsung, Huawei) customizing the OS, and developers contributing via the Android Open Source Project (AOSP). Mathias highlights market dominance: by 2012, Android held significant share, driven by affordability and customization.
Development tools integrate with Eclipse (then primary IDE), using SDK for emulation and debugging. Best practices emphasize modular design to accommodate fragmentation—diverse API levels and hardware. This overview underscores Android’s accessibility for Java developers, bridging desktop/server paradigms to mobile’s event-driven model.
Core Components and Application Structure
Central to Android apps are activities—single screens with user interfaces. Mathias demonstrates starting with a minimal project: manifest.xml declares entry points, main_activity.java handles logic, and layout.xml defines UI via XML or code.
Code for a basic activity:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Intents facilitate inter-component communication, enabling actions like starting activities or services. Explicit intents target specific classes; implicit rely on system resolution.
Services run background tasks, unbound for independence or bound for client interaction. Content Providers expose data across apps, using URIs for CRUD operations. Broadcast Receivers respond to system events.
Mathias stresses lifecycle awareness: methods like onCreate(), onPause(), onDestroy() manage state transitions, preventing leaks.
Handling Asynchronous Operations and Resources
Mobile apps demand responsive UIs; Mathias introduces Handlers and AsyncTasks for off-main-thread work. Handlers post Runnables to UI thread:
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
// UI update
}
});
AsyncTask abstracts background execution with doInBackground(), onPostExecute():
private class DownloadTask extends AsyncTask<String, Integer, String> {
protected String doInBackground(String... urls) {
// Download
return result;
}
protected void onPostExecute(String result) {
// Update UI
}
}
Resources—strings, images, layouts—are externalized in res/ folder, supporting localization and densities. Access via R class: getString(R.string.app_name).
Data persistence uses SharedPreferences for simple key-values, SQLite for databases via SQLiteOpenHelper.
Advanced Patterns and Testing Considerations
Patterns address lifecycle challenges: Bind threads to activity states using booleans for running/pausing. onRetainNonConfigurationInstance() passes objects across recreations (pre-Fragments).
For REST services, use HttpClient or Volley; sensors via SensorManager.
Testing employs JUnit for units, AndroidJUnitRunner for instrumentation. Maven/Hudson automate builds, ensuring CI.
Implications: These elements foster robust, efficient apps. Lifecycle mastery prevents crashes; async patterns maintain fluidity. In fragmented ecosystems, adaptive resources ensure compatibility, while testing mitigates regressions.
Mathias’s approach demystifies Android, empowering Java devs to innovate in mobile spaces.
Links:
Difference between wait() and sleep() in Java
Today in interview I have also been asked the following question: in Java, what is the difference between the methods wait() and sleep()?
First of all, wait() is a method of Object, meanwhile sleep() is a static method of Thread.
More important: Thread.sleep() freezes the execution of the complete thread for a given time. wait(), on its side, gives a maximum time on which the application is suspended: the waiting period may be interrupted by a call to the method notify() on the same object.
“Synchonized” in a block vs on a method
Today, in recruting interview, I have been asked the following question: what is the difference between the Java reserved word synchronized used on a method and this very word in a block? Happily I have known the answer:
Indeed, synchronized uses an Object to lock on. When a methid is synchronized, this means the current object is the locker.
Eg: this piece of code:
[java] public synchronized void foo(){
System.out.println("hello world!!!");
}
[/java]
is equivalent to that:
[java] public void foo(){
synchronized (this) {
System.out.println("hello world!!!");
}
}
[/java]
Besides, when synchronized is used on a static method, the class itself is the locker.
Eg: this piece of code:
[java] public static synchronized void goo() {
System.out.println("Chuck Norris");
}
[/java]is equivalent to that:
[java] public static void goo() {
synchronized (MyClass.class) {
System.out.println("Chuck Norris");
}
}
[/java]
How to export Oracle DB content to DBUnit XML flatfiles?
Case
From an Agile and TDD viewpoint, performing uni tests on DAO is a requirement. Sometimes, instead of using DBUnit datasets “out of the box”, the developper need test on actual data. In the same vein, when a bug appears on production, isolating and reproducing the issue is a smart way to investigate, and, along the way, fix it.
Therefore, how to export actual data from Oracle DB (or even MySQL, Sybase, DB2, etc.) to a DBUnit dataset as a flat XML file?
Here is a Runtime Test I wrote on this subject:
Fix
Spring
Edit the following Spring context file, setting the login, password, etc.
[xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!– don’t forget to write this, otherwise the application will miss the driver class name, and therfore the test will fail–>
<bean id="driverClassForName" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="java.lang.Class"/>
<property name="targetMethod" value="forName"/>
<property name="arguments">
<list>
<value>oracle.jdbc.driver.OracleDriver</value>
</list>
</property>
</bean>
<bean id="connexion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
depends-on="driverClassForName">
<property name="targetClass" value="java.sql.DriverManager"/>
<property name="targetMethod" value="getConnection"/>
<property name="arguments">
<list>
<value>jdbc:oracle:thin:@host:1234:SCHEMA</value>
<value>myLogin</value>
<value>myPassword</value>
</list>
</property>
</bean>
<bean id="databaseConnection" class="org.dbunit.database.DatabaseConnection">
<constructor-arg ref="connexion"/>
</bean>
<bean id="queryDataSet" class="org.dbunit.database.QueryDataSet">
<constructor-arg ref="databaseConnection"/>
</bean>
</beans>[/xml]
The bean driverClassForName does not look to be used ; anyway, if Class.forName("oracle.jdbc.driver.OracleDriver") is not called, then the test will raise an exception.
To ensure driverClassForName is created before the bean connexion, I added a attribute depends-on="driverClassForName". The other beans will be created after connexion, since Spring will deduce the needed order of creation via the explicit dependency tree.
Java
[java]public class Oracle2DBUnitExtractor extends TestCase {
private QueryDataSet queryDataSet;
@Before
public void setUp() throws Exception {
final ApplicationContext applicationContext;
applicationContext = new ClassPathXmlApplicationContext(
"lalou/jonathan/Oracle2DBUnitExtractor-applicationContext.xml");
assertNotNull(applicationContext);
queryDataSet = (QueryDataSet) applicationContext.getBean("queryDataSet");
}
@Test
public void testExportTablesInFile() throws DataSetException, IOException {
// add all the needed tables ; take care to write them in the right order, so that you don’t happen to fall on dependencies issues, such as ones related to foreign keys
queryDataSet.addTable("MYTABLE");
queryDataSet.addTable("MYOTHERTABLE");
queryDataSet.addTable("YETANOTHERTABLE");
// Destination XML file into which data needs to be extracted
FlatXmlDataSet.write(queryDataSet, new FileOutputStream("myProject/src/test/runtime/lalou/jonathan/output-dataset.xml"));
}
}[/java]
How to Read a BLOB for a Human Being?
Case
I have had to access a BLOB and read its content. By principle, I dislike using binary objects, which do not suit easy tracing and auditing. Anyway, in my case, floats are stored in a BLOB, and I need read them in order to validate my current development.
You have many ways to read the content of the BLOB. I used two: SQL and Java
SQL
Start your TOAD for Oracle ; you can launch queries similar to this:
[sql]SELECT UTL_RAW.cast_to_binary_float
(DBMS_LOB.submyrecord (myrecord.myrecordess,
4,
1 + (myrecordessnameid * 4)
)
) AS myrecordessvalue
FROM mytable myrecord
WHERE myrecordessid = 123456; [/sql]
You can also run a stored procedure, similar to this:
[sql]
DECLARE
blobAsVariable BLOB;
my_vr RAW (4);
blobValue FLOAT;
bytelen NUMBER := 4;
v_index NUMBER := 5;
jonathan RAW (4);
loopLength INT;
BEGIN
SELECT myField
INTO blobAsVariable
FROM myTable
WHERE tableid = (5646546846);
DBMS_LOB.READ (blobAsVariable, bytelen, 1, jonathan);
loopLength := UTL_RAW.cast_to_binary_integer (jonathan);
FOR rec IN 1 .. loopLength
LOOP
DBMS_LOB.READ (blobAsVariable, bytelen, v_index, my_vr);
blobValue := UTL_RAW.cast_to_binary_float (my_vr);
v_index := v_index + 4;
DBMS_OUTPUT.put_line (TO_CHAR (blobValue));
END LOOP;
END;[/sql]
Java
I am still not sure to be DBA expert. Indeed I am convinced I am more fluent in Java than in PL/SQL 😉
Create a Spring configuration file, let’s say BlobRuntimeTest-applicationContext.xml:
[xml]<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!– $Id: BlobRuntimeTest-applicationContext.xml $ –>
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@myDBserver:1234:MY_SCHEMA"/>
<property name="username" value="jonathan"/>
<property name="password" value="lalou"/>
<property name="initialSize" value="2"/>
<property name="minIdle" value="2"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>[/xml]
Now create a runtime test:
[java]/**
* User: Jonathan Lalou
* Date: Aug 7, 2011
* Time: 5:22:33 PM
* $Id: BlobRuntimeTest.java $
*/
public class BlobRuntimeTest extends TestCase {
private static final Logger LOGGER = Logger.getLogger(BlobRuntimeTest.class);
private static final String TABLE = "jonathanTable";
private static final String PK_FIELD = "jonathanTablePK";
private static final String BLOB_FIELD = "myBlobField";
private static final int[] PK_VALUES = {123, 456, 789};
private ApplicationContext applicationContext;
private JdbcTemplate jdbcTemplate;
@Before
public void setUp() throws Exception {
applicationContext = new ClassPathXmlApplicationContext(
"lalou/jonathan/the/cownboy/BlobRuntimeTest-applicationContext.xml");
assertNotNull(applicationContext);
jdbcTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
assertNotNull(jdbcTemplate);
}
@After
public void tearDown() throws Exception {
}
@Test
public void testGetArray() throws Exception {
for (int pk_value : PK_VALUES) {
final Blob blob;
final byte[] bytes;
final float[] floats;
blob = (Blob) jdbcTemplate.queryForObject("select " + BLOB_FIELD + " from " + TABLE + " where " + PK_FIELD + " = " + pk_value, Blob.class);
assertNotNull(blob);
bytes = blob.getBytes(1, (int) blob.length());
// process your blob: unzip, read, concat, add, etc..
// floats = ….
LOGGER.info("Blob size: " + floats.length);
LOGGER.info(ToStringBuilder.reflectionToString(floats));
}
}
}
[/java]
Thread leaks in Mule ESB 2.2.1
Abstract
The application I work on packages Mule ESB 2.2.1 in a WAR and deploys it under a WebLogic 10.3 server. My team mates and I noticed that, on multiple deploy/undeploy cycles, the PermGen size dramatically decreased. The cause of this was the number of threads, which hardly decreased on undeployment phases, unlike the expected behaviour.
Indeed, Mule is seldom deployed as a WebApp. Rather, it is designed to be run as a standalone application, within a Tanuki wrapper. When the JVM is killed, all the threads are killed, too, and therefore no thread survives ; hence, the memory is freed and there is no reason to fear a thread leak.
Moreover, when the application is redeployed, new threads -with the same names as the “old” threads- are created. The risk is that, for any reason, a thread-name-based communication between threads may fail, because the communication pipe may be read by the wrong thread.
In my case: on WebLogic startup, there are 31 threads ; when the application is deployed, there are 150 ; when the application works (receives and handles messages), the number of threads climbs to 800 ; when the application is undeployed, only 12 threads are killed, the other remaining alive.
The question is: how to kill Mule-created threads, in order to avoid a Thread leak?
WebLogic Threads
I performed a thread dump at WebLogic startup. Here are WebLogic threads, created before any deployment occurs:
[java]Attach Listener
DoSManager
DynamicListenThread[Default[1]]
DynamicListenThread[Default]
ExecuteThread: ‘0’ for queue: ‘weblogic.socket.Muxer’
ExecuteThread: ‘1’ for queue: ‘weblogic.socket.Muxer’
ExecuteThread: ‘2’ for queue: ‘weblogic.socket.Muxer’
Finalizer
JMX server connection timeout 42
RMI Scheduler(0)
RMI TCP Accept-0
RMI TCP Connection(1)-127.0.0.1
RMI TCP Connection(2)-127.0.0.1
Reference Handler
Signal Dispatcher
Thread-10
Thread-11
Timer-0
Timer-1
VDE Transaction Processor Thread
[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’
[ACTIVE] ExecuteThread: ‘2’ for queue: ‘weblogic.kernel.Default (self-tuning)’
[STANDBY] ExecuteThread: ‘1’ for queue: ‘weblogic.kernel.Default (self-tuning)’
[STANDBY] ExecuteThread: ‘3’ for queue: ‘weblogic.kernel.Default (self-tuning)’
[STANDBY] ExecuteThread: ‘4’ for queue: ‘weblogic.kernel.Default (self-tuning)’
[STANDBY] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’
main
weblogic.GCMonitor
weblogic.cluster.MessageReceiver
weblogic.time.TimeEventGenerator
weblogic.timers.TimerThread
[/java]
Dispose Disposables, Stop Stoppables…
The application being deployed in a WAR, I created a servlet implementing ServletContextListener. In the method contextDestroyed(), I destroy Mule objects (Disposable, Stoppable, Model, Service, etc.) one per one.
Eg#1:
[java] final Collection<Model> allModels;
try {
allModels = MuleServer.getMuleContext().getRegistry().lookupObjects(Model.class);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Disposing models " + allModels.size());
}
for (Model model : allModels) {
model.dispose();
}
allModels.clear();
} catch (Exception e) {
LOGGER.error(e);
}[/java]
Eg#2:
[java] private void stopStoppables() {
final Collection<Stoppable> allStoppables;
try {
allStoppables = MuleServer.getMuleContext().getRegistry().lookupObjects(Stoppable.class);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Stopping stoppables " + allStoppables.size());
}
for (Stoppable stoppable : allStoppables) {
stoppable.stop();
}
allStoppables.clear();
} catch (MuleException e) {
LOGGER.error(e);
}
}[/java]
This first step is needed because default mechanism is flawed: Mule re-creates objects that were destroyed.
Kill Threads
The general idea to kill Mule threads is the following: perform a Unix-style “diff” between WebLogic native threads, and the threads still alive once all Mule objects have been stopped and disposed.
On Application Startup
In the ServletContextListener, I add a field that will be set in a method called in the constructor:
[java] private List<String> threadsAtStartup;
(…)
/**
* This method retrieves the Threads present at startup: mainly speaking, they are Threads related to WebLogic.
*/
private void retrieveThreadsOnStartup() {
final Thread[] threads;
final ThreadGroup threadGroup;
threadGroup = Thread.currentThread().getThreadGroup();
try {
threads = retrieveCurrentActiveThreads(threadGroup);
} catch (NoSuchFieldException e) {
LOGGER.error("Could not retrieve initial Threads list. The application may be unstable on shutting down ", e);
threadsAtStartup = new ArrayList<String>();
return;
} catch (IllegalAccessException e) {
LOGGER.error("Could not retrieve initial Threads list. The application may be unstable on shutting down ", e);
threadsAtStartup = new ArrayList<String>();
return;
}
threadsAtStartup = new ArrayList<String>(threads.length);
for (int i = 0; i < threads.length; i++) {
final Thread thread;
try {
thread = threads[i];
if (null != thread) {
threadsAtStartup.add(thread.getName());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("This Thread was available at startup: " + thread.getName());
}
}
} catch (RuntimeException e) {
LOGGER.error("An error occured on initial Thread statement: ", e);
}
}
}
/**
* Hack to retrieve the field ThreadGroup.threads, which is package-protected and therefore not accessible
*
* @param threadGroup
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private Thread[] retrieveCurrentActiveThreads(ThreadGroup threadGroup) throws NoSuchFieldException, IllegalAccessException {
final Thread[] threads;
final Field privateThreadsField;
privateThreadsField = ThreadGroup.class.getDeclaredField("threads");
privateThreadsField.setAccessible(true);
threads = (Thread[]) privateThreadsField.get(threadGroup);
return threads;
}
[/java]
On application shutdown
In the method ServletContextListener.contextDestroyed(), let’s call this method:
[java] /**
* Cleanses the Threads on shutdown: theorically, when the WebApp is undeployed, should remain only the threads
* that were present before the WAR was deployed. Unfornately, Mule leaves alive many threads on shutdown, reducing
* PermGen size and recreating new threads with the same names as the old ones, inducing a kind of instability.
*/
private void cleanseThreadsOnShutdown() {
final Thread[] threads;
final ThreadGroup threadGroup;
final String currentThreadName;
currentThreadName = Thread.currentThread().getName();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("On shutdown, currentThreadName is: " + currentThreadName);
}
threadGroup = Thread.currentThread().getThreadGroup();
try {
threads = retrieveCurrentActiveThreads(threadGroup);
} catch (NoSuchFieldException e) {
LOGGER.error("An error occured on Threads cleaning at shutdown", e);
return;
} catch (IllegalAccessException e) {
LOGGER.error("An error occured on Threads cleaning at shutdown", e);
return;
}
for (Thread thread : threads) {
final String threadName = thread.getName();
final Boolean shouldThisThreadBeKilled;
shouldThisThreadBeKilled = isThisThreadToBeKilled(currentThreadName, threadName);
if (LOGGER.isDebugEnabled()) {
LOGGER.info("should the thread named " + threadName + " be killed? " + shouldThisThreadBeKilled);
}
if (shouldThisThreadBeKilled) {
thread.interrupt();
thread = null;
}
}
}
/**
* Says whether a thread is to be killed<br/>
* Rules:
* <ul><li>a Thread must NOT be killed if:</li>
* <ol>
* <li>it was among the threads available at startup</li>
* <li>it is a Thread belonging to WebLogic (normally, WebLogic threads are among the list in the previous case</li>
* <li>it is the current Thread (simple protection against unlikely situation)</li>
* </ol>
* <li>a Thread must be killed: in all other cases</li>
* </ul>
*
* @param currentThreadName
* @param threadName
* @return
*/
private Boolean isThisThreadToBeKilled(String currentThreadName, String threadName) {
final Boolean toBeKilled;
toBeKilled = !threadsAtStartup.contains(threadName)
&& !StringUtils.contains(threadName, "weblogic")
&& !threadName.equalsIgnoreCase(currentThreadName);
return toBeKilled;
}
[/java]
EhCache
My application uses an EhCache. Its threads names usually end with “.data”. They are not killed by the previous actions. To get rid of them, the most elegant way is to add this block in the web.xml:
[xml] <listener>
<listener-class>net.sf.ehcache.constructs.web.ShutdownListener</listener-class>
</listener>
[/xml]
cf EhCache documentation
With all these operations, almost all threads are killed. But Java VisualVM still displays 34, vs. 31 at startup.
Tough Threads
A thread dump confirms that, at this point, 3 rebellious threads still refuse to be kill:
[java]MuleServer.1
SocketTimeoutMonitor-Monitor.1
SocketTimeoutMonitor-Monitor.1
[/java]
Let’s examine them:
MuleServer.1: This thread is an instance of the inner classMuleServer.ShutdownThread. Indeed, this is the first thread created by Mule, and therefore appears among the threads available at startup, before theServletContextListeneris called… I did not succeed in killing it, even why trying to kill it namely, which makes sense: killing the father thread looks like suiciding theServletContextListener.SocketTimeoutMonitor-Monitor.1: This thread is created by Mule’sTcpConnectorand its daughter classes:HttpConnector,SslConnector, etc. Again, I could not kill them.
Conclusion
We have seen Mule suffers of major thread leaks when deployed as a WAR. Anyway, most of these leaks may be sealed.
I assume MuleSoft was aware of this issue: in the version 3 of Mule, the deployment of webapps was refactored.
How to access non-visible fields in Java?
How to access a non-acccessible field (either protected, package-protected or private) of an object in Java?
For instance, you would like to access the field threads of ThreadGroup:
[java]ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
final Field privateThreadsField;
privateThreadsField = ThreadGroup.class.getDeclaredField("threads");
privateThreadsField.setAccessible(true);
threads = (Thread[]) privateThreadsField.get(threadGroup);[/java]
How to include a dependency to tools.jar in Maven?
Case
You need include tools.jar as a dependency in a pom.xml, for instance in order to use Java 5’s annotations and APT. From a “Maven’s view point”, tools.jar is not a regular JAR defined by a groupId and artefactId.
Solution
Add this block in your pom.xml:
[xml]<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.6.0_24</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>[/xml]
(You can also add it in your settings.xml)
You can do the same for any other “non-regular” JAR, available in your file system.
javac: invalid flag: -s
Case
After updating my project and launching a build with Maven, I got this error:
[java][ERROR] BUILD FAILURE
[INFO] ————————————————————————
[INFO] Compilation failure
Failure executing javac, but could not parse the error:
javac: invalid flag: -s
Usage: javac <options> <source files>[/java]
Fix
Indeed the version of Java in the pom.xml had been upgraded. To get rid of the error, update your $JAVA_HOME to use a JDK 6 and no more a JDK5.
WebLogic: use a startup and/or a shutdown class in a WAR
Abstract
WebLogic offers you to specify a startup and/or a shutdown class for an application. Anyway, this feature is restrained to EARs, and is not available for applications deployed as WARs. For EARs, Oracle WebLogic Server’s documentation is complete and gives basic examples: Programming Application Life Cycle Events
Yet, sometimes you need such a class even for a WebApp. You have two ways to handle this case.
Solutions
Full Weblogic!
Base
The first solution is not elegant. Open WebLogic console, go to Startup And Shutdown Classes, then add the classes names of which main() methods will be run on startup and shutdown, let’s say JonathanWeblogicStartup. These classes must be available in WebLogic classpath, which is surely different from your application classpath, eg in a library $DOMAIN_HOME/lib/customized-weblogic.jar.
Advantage of this means: if your startup/shutdown class does not evoluate often, then write it once and forget, it will be OK. Unlike, you will have to manage different versions of the JAR on each release… I assume your exploitation team may get angry at playing with classpaths and lib folders 😀
Suggestion
To improve the basic solution (and avoid your exploitation guy burst in your office), I had the following idea, that I did not experiment, but that should work:
- Keep
JonathanWeblogicStartup
- Create another class
JonathanConcreteStartup, locate as a source in your application code. - In
JonathanStartup.main(), callJonathanConcreteStartup
This way,
- You keep a unique JAR in classpath that you do not need to update and you can forget.
- You can add, update or remove features in your very source code.
- The exploitation teams does not become hateful at you.
Add a Listener
The second one requires a bit more work.
- Create a class implementing
javax.servlet.ServletContextListenerinterface, let’s sayMyLifecycleListener. - Implement methods
contextInitialized()andcontextDestroyed(). - Edit your web.xml, add the following block:
[xml]
<listener>
<listener-class>lalou.jonathan.MyLifecycleListener</listener-class>
</listener>[/xml] - Rebuild and deploy. It should be OK