Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Tuesday, September 16, 2014

How to secure a password file on Windows 7 (JMX interface of ActiveMQ, to be specific)

This took me a while to figure out, so here's a description of how to make use of a password-protected JMX interface with ActiveMQ (5.8 in my case).

1. Make sure your activemq.xml specifies that you actually want to allow JMX monitoring:
   <managementContext>
        <managementContext createConnector="true" connectorPort="1098"/>
   </managementContext>

2. Change activemq.bat startup script to specify an explicit password files:

set SUNJMX=-Dcom.sun.management.jmxremote.port=1098
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.password.file=%ACTIVEMQ_BASE%/conf/jmx.password
-Dcom.sun.management.jmxremote.access.file=%ACTIVEMQ_BASE%/conf/jmx.access

when you start ActiveMQ, you will probably get this error now:

> activemq.bat
Error: Password file read access must be restricted: .../conf/jmx.password

ActiveMQ requires the password file to have specific user-only permissions, see here for more information. Unfortunately this link is for Windows XP, so here's what to do on Windows 7

I've actually found two solutions, one graphical, the other one from the command line:

Solution (using Windows Explorer):

1) change the owner to be 'you' (required step!!)
Select jmx.password, Right-Mouse-Cick -> Properties -> Security -> Advanced -> Owner -> Edit
and select the single owner of this to be your username.

Note: you need to click OK and exit out of Properties for this to be effective

2) Select jmx.password, Right-Mouse-Cick -> Properties -> Security -> Advanced -> Change Permissions 

- uncheck "Include inheritable permissions" and click Remove to remove all inherited permissions
- then click Add... to add read/write permissions for only your user: Enter your username as object name, and select for example 'Full Control'. Click Ok and exit out of properties.


Solution (using Windows command line):

1) open a windows command prompt in your ActiveMQ 'conf' folder.


2) use icacls (run 'icacls' without options for help) to change the owner to be 'you', in my case:

icacls jmx.password /setowner apodehl


3) remove all inherited permissions:

icacls jmx.password /inheritance:r


4) grant minimal permissions to your user (read/write in this case):

icacls jmx.password /grant:r apodehl:(r,w)



Monday, June 2, 2014

How to use JDBC-ODBC in a 64-bit JVM with a 32-bit version of Office

When using the JDBC-ODBC bridge in the JDK to access Microsoft Access files, you would set your JDBC class to sun.jdbc.odbc.JdbcOdbcDriver and your JDBC URL, for example, to:

"jdbc:odbc:Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=C:\CodeStreet\sample.accdb"

So far so good, but in case you are running a 64-bit JVM with a 32-bit Microsoft Office installation, your JVM and Access driver architecture don't match  and you would see error messages such as:
java.sql.SQLException: [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified


or
"The setup routines for the Microsoft Access Driver .. could not be found."

Fortunately, there are now 64-bit Microsoft Access drivers available, but using them in this context is quite tricky. Once you install the drivers, Microsoft Office stops working !

Opening an Excel file, for example, tries to find the 64-bit version of Office, which you don't have :-(

Instead of opening a file, you will see "Configuration Progress" and "Configuring Microsoft Office Professional Plus 2010..." - what the heck ?

But there's a neat little workaround which goes like this: 

1. Download the Microsoft Access drivers  

2. Check this registry key:
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\14.0\Common\FilesPaths 

If if currently contains an entry with mso.dll, you are using Office 64-bit, which is ok. If there is NO mso.dll key then your Office version is 32-bit. 

3. Open a command prompt and install the 64-bit driver in passive mode (it won't let you do this any other way):
    AccessDatabaseEngine_X64.exe /passive

4. If  mso.dll was not in your registry in step 2, then remove this key now from
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\14.0\Common\FilesPaths 


Microsoft Office (32-bit) should start working again and your 64-bit Access drivers are ready to go.



Good Luck!
 

Monday, December 3, 2012

Debugging Java Thread CPU usage

In case you want to quickly find out which of your Java threads is the culprit in a JVM's high CPU usage, this tool is truly helpful.

Simply download topthreads.jar from http://lsd.luminis.nl/top-threads-plugin-for-jconsole/
and run your jconsole like this:

     jconsole -pluginpath topthreads.jar

Similar to 'jtop', just a little better.




Tuesday, July 12, 2011

Eclipse doesn't start after crash

After a crash on Windows 7, my beloved Eclipse environment wouldn't start again! It just hung there, showing nothing but the Splash screen. eclipse -clean didn't help, monitoring files with Sysinternal's ProcMon or 'handle.exe' didn't help - quite devastating... But finally I found the solution here:
  1. cd to your Eclipse home directory
  2. cd .metadata/.plugins
  3. ren org.eclipse.core.resources BAK (Keep this directory around)
  4. Restart Eclipse, ignore the error message.
  5. Close all open editors tabs !! (in my case, playing with the .xsd file editor caused the issue)
  6. Exit Eclipse.
  7. del org.eclipse.core.resources (Delete the newly created directory.)
  8. ren BAK org.eclipse.core.resources (Restore the original directory.)
  9. Restart Eclipse.

Wednesday, December 15, 2010

How to implement a 'repeating' Swing JButton ?

Sometimes you want to repeat your JButton action after the button was pressed. This is definitely not rocket-science, but I thought it's nice and small enough to share. Here's the usual code with a one-click action: a 'Next' button that selects the next row in a table.

    nextButton.addActionListener( new ActionListener() {
      public void actionPerformed(ActionEvent arg0)
      {
        selectNextRow(...);
      }});
For a repeating button, you can replace this code with the code snippet below. Adjust the times appropriately. I wonder why this isn't standard functionality in a Swing JButton(..., startMsec, repeatMsec) ?

    nextButton.addMouseListener( new MouseAdapter() {
      
      Timer repeatTimer;
      
      public void mousePressed(MouseEvent arg0)
      {
        selectNextRow(...); // initial execution
        
        repeatTimer = new Timer(100, new ActionListener() {
          public void actionPerformed(ActionEvent e)
          {
            selectNextRow(...); // execute every 100 msec
          }});
        repeatTimer.setInitialDelay(1000); // start repeating only after 1 second
        repeatTimer.start();
      }
      
      public void mouseReleased(MouseEvent arg0)
      {
        repeatTimer.stop();
      }});

Monday, February 15, 2010

Java: Sorted List

Sometimes you might have the need for a Java list, that is sorted with every element insertion. If you use a sorted TreeSet, you are stuck with iterators, no random access for elements. If you use an ArrayList, you could call Collections.sort() after every add, but that's not necessarily efficient. Collection.sort() will have to check the order for every element. If your list is long, this might take time.
But here's the trick: if you know your list was already sorted before your insertion, there's no need to re-sort everything again. You can find the proper insertion point with a quick binary search, and insert the new element at the proper position. VoilĂ , your list is always sorted.
Surprisingly simple to do this in Java:



/** A LinkedList that efficiently sorts itself with every add.
 * 
 * @author apodehl
 *
 */
public class SortedList< T extends Comparable> extends LinkedList
{
  /** Adds this element to the list at the proper sorting position.
   * If the element already exists, don't do anything.
   */
  @Override
  public boolean add(T e)
  {
    if( size()==0 ) {
      
      return super.add(e);
    }
    else {
      
      // find insertion index
      int idx = -Collections.binarySearch(this, e)-1;
      
      if( idx < 0 ) {
        return true; // already added
      }
      
      // add at this position
      super.add(idx, e);
      return true;
    }
  }
}



Java BiMap, BidiMap

After doing some research and trying to find just the right Java Bi-Map, I couldn't find any, so I had to roll my own :-( To my surprise, implementation of a Bi-Maps is not straight-forward, there's not such thing as the 'one Bi-Map functionality'.
The interesting case is this one: say there are mappings A->1 and B->2. What happens once you insert C->2 ?
Will you remove B->2 to ensure uniqueness of keys and values ? (resulting in A->1 and C->2)
or not, resulting in A->1, B->2 and C->2. Please note that in the latter case, there's no well-defined lookup for value "2". Is it B or is it C ?
So what I needed was uniqueness of both keys and values, in all cases, using generics. Here it goes:


import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * One possible implementation of a Bi-directional Map.
 * 
 * Note: there are several variations of BiMap, depending on the behaviour of put(k,v)... Is it allowed to have more
 * than one k for one value ?? Modify
 * 
 * @author apodehl
 * 
 * @param 
 * @param 
 */
public class BiMap implements Map
{
  Map keyVal; // maps key->value

  Map valKey; // maps value->key

  public BiMap()
  {
    keyVal = new LinkedHashMap();
    valKey = new LinkedHashMap();
  }

  @Override
  public V put(K key, V val)
  {
    // --- this implementation allows one-to-one ONLY !! ---
    if (valKey.containsKey(val)) {
      keyVal.remove(valKey.get(val));
    }
    if (keyVal.containsKey(key)) {
      valKey.remove(keyVal.get(key));
    }
    // --- remove above if that's not what you want ---

    // 
    valKey.put(val, key);
    return keyVal.put(key, val);
  }

  @Override
  public void putAll(Map< ? extends K, ? extends V> m)
  {
    for (Entry< ? extends K, ? extends V> e : m.entrySet()) {
      put(e.getKey(), e.getValue());
    }
  }

  /**
   * Type-safe get.
   * 
   * @param key
   * @return
   */
  public K getKey(V val)
  {
    return valKey.get(val);
  }

  /**
   * Type-safe get.
   * 
   * @param key
   * @return
   */
  public V getVal(K key)
  {
    return keyVal.get(key);
  }

  @Override
  // unfortunately Map interface isn't type-safe
  public V get(Object key)
  {
    return getVal((K) key);
  }

  /**
   * Type-safe contains.
   * 
   * @param key
   * @return
   */
  public boolean contains(K key)
  {
    if (keyVal == null) return false;
    return keyVal.containsKey(key);
  }

  /**
   * Type-safe containsValue.
   * 
   * @param key
   * @return
   */
  public boolean containsVal(V val)
  {
    if (valKey == null) return false;
    return valKey.containsKey(val);
  }

  // unfortunately Map interface isn't type safe here ..
  @Override
  public boolean containsKey(Object key)
  {
    return contains((K) key);
  }

  // unfortunately Map interface isn't type safe here ..
  @Override
  public boolean containsValue(Object value)
  {
    return containsVal((V) value);
  }

  @Override
  public Set< java.util.Map.Entry> entrySet()
  {
    return keyVal.entrySet();
  }

  @Override
  public boolean isEmpty()
  {
    return keyVal.isEmpty();
  }

  @Override
  public int size()
  {
    return keyVal.size();
  }

  @Override
  public Collection values()
  {
    return keyVal.values();
  }

  public V removeByKey(K key)
  {
    V val = keyVal.remove(key);
    valKey.remove(val);
    return val;
  }

  public K removeByVal(V val)
  {
    K key = valKey.remove(val);
    keyVal.remove(key);
    return key;
  }

  // unfortunately Map interface isn't type-safe
  @Override
  public V remove(Object key)
  {
    return removeByKey((K) key);
  }

  @Override
  public Set keySet()
  {
    return keyVal.keySet();
  }

  @Override
  public void clear()
  {
    keyVal.clear();
    valKey.clear();
  }

  public String toString()
  {
    String s = "BiMap:\n";
    s += "  key->value: " + keyVal.toString();
    s += "\n";
    s += "  value->key: " + valKey.toString();
    return s;
  }

  public static void main(String[] args)
  {

    BiMap biMap = new BiMap();
    biMap.put("A", 1);
    biMap.put("B", 2);
    System.out.println(biMap); // {A=1, B=2}

    biMap.put("C", 2);
    System.out.println(biMap); // {A=1, C=2}

    biMap.removeByVal(1);
    System.out.println(biMap); // {C=2}
  }
}

Thursday, October 29, 2009

Automatic Restart Script for a Java service

This blog tries to describe a pattern on how to write a pragmatic Unix start and stop script for an automatically restarting Java service.
In many cases, you might want to write a Java service that should be up and running 24x7. Now in theory the garbage collector should deal with everything, and if programmed correctly, the process should never crash. But in praxis, things do happen. For example a web service could encounter user requests where memory use is much bigger than you ever expected.
What is a good maximum heap size anyway ?
A pragmatic approach is just to face the fact that your JRE could run out of memory and deal with it. There are sophisticated monitoring solutions out there to automatically restart processes (e.g. nagios/ganglia), but a poor-man's solution is to automatically restart the JRE from the Unix start script.
Please note, you don't want to restart in every case. A bad command line option should just stop the process and not run into the restar loop. Also, there must be a clean way to manually stop it.
Under these constraints, the best solution I could find is to create a temporary file from the Java code at exactly the point 'of no return'. If the JRE stops before this point, no restart happens. If the JRE stops after this point, automatic restart will kick in.
... parse command line options ...
 
     // register shutdown hook      Runtime.getRuntime().addShutdownHook(new ShutdownHook(...));
     // register uncaught exception handler
    Thread.currentThread().setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler() {
      public void uncaughtException(Thread t, Throwable e)
      {
        e.printStackTrace();
        if( e.getClass()==java.lang.OutOfMemoryError.class ) {
      
          System.err.println("FATAL: shutting down because of java.lang.OutOfMemoryError ...");
          System.exit(7);
        }
      }} );

    File restart = new File("webservice.restart"); // tell the shell to restart me
    restart.createNewFile();

... start your service ...
The Unix start script below will restart the JRE if, and only if, the temporary file (webservice.restart) exists. This could go like this:
#!/bin/sh

#try to start service once
${JAVA_HOME}/bin/java -DREPLAYWEB ${JOPT} com.codestreet.replay.jms.shell.web.ReplayWeb $*

# restart again (until webservice.restart file was removed)
while [ -f webservice.restart ]; do
echo "### RESTART ###"
/bin/rm -f webservice.restart # let Java decide if we really want to restart
${JAVA_HOME}/bin/java -DREPLAYWEB ${JOPT} com.codestreet.replay.jms.shell.web.ReplayWeb $*
done

So Java decides if the service should be restarted and the shell actually performs the restart.
An alternative would have been to simply use return codes from System.exit() ?
But then the question would be: what's the return code with a not yet known exception ?
If someone else uses kill-9 on the jre, a shutdown hook wouldn't be invoked. And to manually stop the restarting you would have to kill the start script as well as the JRE.
With this file-based approach, the stop script is pretty simple: remove the temporary file and kill the JRE. Please note that finding the proper Java process is not as simple as it seems since the classpath is usually very long and 'ps -f' potentially won't show the classname anymore because the line gets too long. On Linux you can use the --col option to see a longer output, but that doesn't work on Solaris :-(
So a little trick around this is to use a dummy JRE property, -DREPLAYWEB in this case. This mock up property has no meaning except that it will show up in ps before the classpath and you can make it unique enough to identify only this instance of JRE.
The stop script would then perform these steps:
  1. get the 'ps line' that contains the dummy JRE property (REPLAYWEB)
  2. get the task id of that process (awk is good enough)
  3. remove the temporary restart file so the start script won't restart automatically
  4. kill the process
#!/bin/sh -f

psline=`/bin/ps -aef | /bin/grep "REPLAYWEB" | /bin/grep -v grep`
echo $psline
pid=`echo $psline | /bin/awk '{ print $2}'`
if [ $pid ]
then
 /bin/rm -f webservice.restart
 echo "stop_webservice: killing Web Service with pid=$pid"
 kill $pid
else
 echo "stop_webservice: Web Service was not running"
fi

Thursday, October 15, 2009

JAX-WS inside Jetty

Jetty is a nice and easy to use Web Server (see previous post), but it can it handle an open standards, heavy weight JAX-WS Web Service ?
With a bit of luck and glue code, it can.
So first we need a Service Provider Implementation (SPI) from Jetty: J2se6HttpServerSPI
This will make the JAX-WS endpoint use the Jetty server instead its default Sun HttpServer.
To plugin different SPI's you would define this new service in a META-INF/services file, but you can also set a system property, as described here. You can even do this in code which reduces the number of files you need to worry about when refactoring.
So here's an example of a Jetty Server handling a JAX-WS endpoint in combination with a File system directory.
      Server jettyServer = new Server(port);
     HandlerCollection handlerCollection = new HandlerCollection();
     jettyServer.setHandler(handlerCollection);
    
     /** 1) Publish WebService (JettyHttpServerProvider) */ 
     String context = "/web/ws";
             
     // 1.1) register THIS Jetty server with the JettyHttpServerProvider
     new JettyHttpServerProvider().setServer(jettyServer);
      
     // 1.2) make sure JAX-WS endpoint.publish will use our new service provider: JettyHttpServerProvider
     System.setProperty("com.sun.net.httpserver.HttpServerProvider",
           "org.mortbay.jetty.j2se6.JettyHttpServerProvider");
      
     // 1.3) add an empty HandlerCollection to by setup by this provider
     handlerCollection.addHandler( new HandlerCollection() );
      
     // 1.4) use JAX-WS API to publish the endpoint (will use a JettyHttpServerProvider)
     Endpoint endpoint = Endpoint.create(replayServiceImpl);
     endpoint.publish("http://localhost:" + port + "/web/ws", replayServiceImpl);
    
     /** 2) Publish WebGUI (Jetty) */       
     String context = "/gui";

     // 2.1) configure File Resource Handler
     ResourceHandler fileResourceHandler=new ResourceHandler();
     fileResourceHandler.setWelcomeFiles(new String[]{"index.html"});
     fileResourceHandler.setResourceBase(guiPath); // start here
       
     // 2.2) configure 'gui' Context
     ContextHandler guiHandler = new ContextHandler();
     guiHandler.setContextPath(context);
     guiHandler.setResourceBase(".");
     guiHandler.setClassLoader(Thread.currentThread().getContextClassLoader());
     guiHandler.setHandler(fileResourceHandler);
      
     // 2.3) add this context handler
     handlerCollection.addHandler(guiHandler);
 
     /** 3) start JETTY server */
     jettyServer.start();
     jettyServer.join();

Embedding an HTTP daemon in Java

Apache Tomcat, the well-known WebServer is about 84MB after installation. Other contenders are even bigger. Does it have to be so complicated ? The following blog is about the search for a 'right size' HTTP daemon embedable in Java.
Now, what's a Web Server anyway ?
To my mind, its just a piece of software that opens a socket (port 8080) where you send requests to (as defined by the HTTP protocol) and it returns data, in most cases something from the file system (HTML pages and such). This doesn't sound too complicated. In fact, there are examples on the net of HTTP daemons that only take 10 lines of source code (compare the C++ obfuscation contest).
But besides this little niche, there is also NanoHTTPD which already handles some of the ugliness of HTTP. So in order to serve up a system directory, which could contain fairly complicated AJAX/Javascript/JSP files, you don't need more than this single file of Java source code.
If serving a file directory is all you need, go back to the previous chapter.
Unfortunately, things are not that easy ... sometimes you also need to publish a WebService or a Servlet or an already packaged Web application (a war file). Then you need something slightly bigger. The best thing I could find in this category is Jetty. It gives up a nice stand-alone WebServer with all kinds of configuration choices and extension points, but most importantly, it is very easy to embed. By using three jar files (less then 1 MB in total) and writing very little code you again have a complete HTTP daemon. Oh, and also, Jetty is already used inside Eclipse and GWT.

Publishing a JAX-WS Endpoint not just to localhost

Interestingly enough, publishing the JAX-WS Endpoint with endpoint.publish(url) as described in a previous post, publishes the WSDL to http://localhost:8080 but this socket is not accessible from any other host ! Strange default, but there you go ...
So in order to really make a JAX-WS Endpoint public to the world, you can revert to the slightly more cumbersome API of com.sun.net.httpserver.HttpServer (at least on Sun's JDK):

HttpServer server = HttpServer.create(new InetSocketAddress(port), 3 ); // backlog
server.start();
  
endpoint.setExecutor( Executors.newFixedThreadPool(10) );
  
// publish WebService
HttpContext wsContext = server.createContext( "/" + service );
  
//wsContext.setAuthenticator(new TestBasicAuthenticator("test"));
endpoint.publish( wsContext );

Monday, September 7, 2009

60 seconds on SOAP-based Web Services

According to this book, there are 74 distinct initiatives trying to define what a Web Service should look like. And I bet, there are at least 74 different abbreviations around to describe different parts of it. So it's surprising how simple it is to create a Web Service with JDK 1.6. First you define an interface with a bunch of annotations:

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

@WebService
// use RPC or 'wrapped' document-style
@SOAPBinding(style = Style.RPC)
//@SOAPBinding(style = Style.DOCUMENT,
// parameterStyle = SOAPBinding.ParameterStyle.WRAPPED )
public interface Example
{
 @WebMethod public String getGreeting( @WebParam(name="myname") String myname);
}
And then implement the service itself. Use a thread-pool if your application is multi-threaded.

import javax.jws.WebService;
import javax.xml.ws.Endpoint;

@WebService(endpointInterface = "simple.Example")
public class ExampleImpl implements Example
{
 @Override
 public String getGreeting(String myname)
 {
   return "hello, " + myname;
 }

 public static void main(String[] args)
 {
   String url = "http://127.0.0.1:9876/example";
   System.out.println("Starting WebService at: " + url);
   System.out.println("WSDL available at:      " + url + "?wsdl");

   Endpoint endpoint = Endpoint.create(new ExampleImpl());
   //endpoint.setExecutor( Executors.newFixedThreadPool(10) );
   endpoint.publish(url);
 }
}
The resulting WSDL definition of this service can be looked at http://127.0.0.1:9876/example?wsdl (use Firefox or IE, not Chrome!). Any SOAP-based client (such as the XML Test Utility in General Interface, see previous post) can now invoke this service.
REST-style would be harder to implement, but easier to test. In the REST case, you can simply do: http://127.0.0.1:9876/rs?name="Pete" for invocations. The difference to the previous solution is that with REST-style, the client uses an HTTP GET to invoke a method and the return value can be any kind of XML, not just a SOAP envelope.
The code above implements the main 'standard', but its also the most bloated possibilty. More efficient, but less standardized, is a message encoding of JSON instead of XML, or better, use the Google Web Toolkit. If you are in full control of the server and the client and don't need to follow standards, GWT seems an excellent choice right now.

Monday, August 31, 2009

Java Bi-directional Maps: Apache commons or Google collections ?

In case you need a bi-directional Map in Java, you can, of course, easily roll your own.
But this is a common problem, so others have done this work already.
Here's a simple how-to on bi-directional maps with Apache and Google commons.
Apache commons collections offers a BidiMap interface with various implementations.
For example:
public class TestBidiMap
{
static BidiMap bimap = new DualHashBidiMap();
public static void main(String[] args)
{
bimap.put("D", 3);
System.out.println("D -> " + bimap.get("D") );
System.out.println("3 -> " + bimap.inverseBidiMap().get(3) );
}
}
Nice and easy, but unfortunately this version of BidiMap doesn't use Java generics, which I have learned to like over time (bimpap.get(3) will not show a compile error) !
Google collections has an alternative that uses generics: BiMap
public class TestBiMap
{
static BiMap<String,Integer> bimap = HashBiMap.create();
public static void main(String[] args)
{
 bimap.put("D", 3);
 System.out.println("D -> " + bimap.get("D") );
 System.out.println("3 -> " + bimap.inverse().get(3) );
}
}
Also nice and easy, what's interesting is the factory method HashBiMap.create(). This removes the need to duplicate the generics type specification of String, Integer. Kiss rules, so two points for Google collections here.
Summary: any mid-size Java project probably shouldn't live without Apache commons, but as a useful addition, Google collections is definitely worth a look.

Monday, August 10, 2009

Ajax: Xpath-enabled Json queries

In the Ajax world, there is a big discussion about XML versus Jsonas a means of transporting data from the Server to the Browser. After looking at the first examples of using a DOM API in Javascript, I knew, I'll not be using XML in Javascript. Using Json instead, feels very nice and natural. But Json is just a means of transport. Nothing else. The simplisticclasses work, but no query language to select the interesting data before sending it over. If you structure your Web-Gui well, the resulting data should be simple. Hence no need for the complex XML structure. XML offers however, two goodies, I wouldn't want to miss: Object to XML serializers (e.g. XStream) and XPath to select parts of the XML. So my idea is this: why not marry both approaches ? Transform the Java object into XML, apply an XPath query to select what you really want and then transform the result to Json to send it over. Even more flexible, support a 'class' and 'method' (and an optional 'args') parameter to select which data to get. Here is how it works: 1) Ajax Request: On the Browser, request a Json object with parameters class,method, xpath:
requestList( "class=TibjmsAdmin&method=getTopics&xpath=/*/*/name",
"name",
isAsync,
topicList );
2) Java Method call: On the Server-side, this request will execute the method TibjmsAdmin.getTopics() and create a Java object with some code similar to this:

if( className.equals("TibjmsAdmin") ) { // TODO: use ClassLoader (security?)
 Object invokee = jmsAdmin;
}
... others ...

// get specified method on specified class
className = invokee.getClass().getName();
Class c = Class.forName(className); // className is TibjmsAdmin
Method m = c.getMethod(method,argClasses); // method is getTopics, argClasses is null

// invoke method and get return object
  returnObj = m.invoke(invokee, argObs);
3) Java to XML: the next step is now to serialize this Java object to XML. I like to use XStream for this purpose because its extremly simple and straight-forward.

XStream xstream = new XStream();
String xml = xstream.toXML(returnObj);
If the returned object does not produce the required results, you can do some magic by plugging in an XStream 'Converter' (not shown here). 4) Apply Xpath: Now that that we have the XML, we can apply the specified XPath to fiddle out the data that is really interesting for us. The following code will create a list of selected XML nodes.
    NodeList nl = XmlUtils.selectNodeList(docBuilder,xmlString,xpath);
StringBuffer sb = new StringBuffer("");
for( int i=0;i<nl.getLength();i++ ) {

  Node node = nl.item(i);
  sb.append("<" + node.getNodeName() + ">" );
  sb.append( node.getTextContent() );
  sb.append("" );
}
sb.append("\n");
5) Return Json result: finally return the Json object back to the Browser using the Json tools.
JSONObject json = XML.toJSONObject(xml);
  response.setContentType("text/plain");
if( out.equals("pretty") ) // pretty JSON
  response.getWriter().print( json.toString(2) );
else // or not
  response.getWriter().print( json.toString() );

Wednesday, July 22, 2009

How to display a non-scrolling background for a Java JScrollPane

To add a background text (or image) to the back of a JTextArea that is inside a JScrollPane, you can use the following code. The advantage here is that the background text/image does NOT scroll with the text in the JTextArea. See screenshot below.
Here's the code:
public static void main(String[] args) {


JFrame frame = new JFrame("Overlay test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JScrollPane scroll = new JScrollPane();
scroll.getViewport().setBackground(Color.WHITE);

scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.setPreferredSize( new Dimension(400,100));
frame.add(scroll);

final String text = "English";

final JTextArea ta = new JTextArea() {

 {setOpaque(false);}  // instance initializer

 public void paintComponent (Graphics g) {
   g.setFont(new Font("Verdana",Font.ITALIC,20));
   g.setColor(Color.LIGHT_GRAY);

   Rectangle rect = getVisibleRect();
   int x = rect.width + rect.x - 14*text.length();
   int y = rect.y + 20; // approx. height of the text
   g.drawString(text, x, y);
   super.paintComponent(g);
 }
};

ta.setText("This text area contains an overlayed text that is BEHIND this text and does not scroll");
ta.setPreferredSize( new Dimension(600,100));
scroll.setViewportView(ta);
frame.pack();
frame.setVisible(true);
}