Oops Null Pointer

Java programming related

GXT: BorderLayout starting with a collapsed region

Calling collapse on a BorderLayout region before it has been rendered doesn’t work. One suggestion was to add a deferred command to collapse the region. I found this was “flashing” the panel (showing and then hiding it) and also that when you pop out a collapsed panel (clicking on the collapsed section) the popup was empty as panel was still effectively collapsed. Instead I used a once only AfterLayout event listener:

layoutContainer.addListener(Events.AfterLayout, new Listener<ComponentEvent>() {
  public void handleEvent(ComponentEvent be) {
    borderLayout.collapse(LayoutRegion.WEST);
    be.getComponent().removeListener(Events.AfterLayout, this);
  }
});

Using this method some child components may have trouble rendering inside the collapsed panel. One such component is a TreePanel – if you add it and then call expandAll() it will throw a JavaScriptException, caused by the parent of the collapsed panel being null.
com.google.gwt.core.client.JavaScriptException: (TypeError): this.appendChild is not a function

To counter this I added a once only Attach event listener to the panel that will be collapsed:

myContentPanel.addListener(Events.Attach, new Listener<ComponentEvent>() {
  public void handleEvent(ComponentEvent ce) {
    myTreePanel.expandAll();
    // Remove this listener - expand only once
    ce.getComponent().removeListener(Events.Attach, this);
  }
});
Advertisements

XStream Removing no-comparator from XML output

When XStream exports a TreeSet to XML the output contains “no-comparator” and “comparator” nodes. For my clients the comparator node is not used and makes the XML less clear.

To remove these nodes from the output I added a customer converter:

public class IgnoreComparatorTreeSetConverter extends CollectionConverter {
    public IgnoreComparatorTreeSetConverter(Mapper mapper) {
        super(mapper);
    }
    
    @Override
    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
        return type.equals(TreeSet.class);
    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        TreeSet<Object> result = new TreeSet<Object>();
        super.populateCollection(reader, context, result);
        return result;
    }
}

And to use it just register the converter:

XStream xstream = new XStream(new DomDriver());
xstream.setMode(XStream.NO_REFERENCES);
xstream.registerConverter(new IgnoreComparatorTreeSetConverter(xstream.getMapper()));

Ehcache Error: Could not create cache directory

I recently updated ehcache from 2.3.0 to 2.4.2. Everything seemed to work fine, but one cache was throwing the error “Could not create cache directory” and the location was in the bin directory of my app server. Suspiciously, this is the default path when an empty path is given as the disk store location. This cache is a little unusual in it’s configuration as it loads an xml config file for the cache manager settings and then programmatically adds a cache to this cache manager. The xml file has the disk store path and the cache constructor sets the disk store parameter to “”.

In 2.3.0 this would allow the cache to inherit the disk sore in the xml cache manager config, but in 2.4.2 you must set the disk store parameter in null to have the same effect. I couldn’t find this noted anywhere so I though I’d post it here in case you hit the same pot hole on the road to cache happiness.

This upgrade was part of heading towards using Terracotta to cluster application (via the caches) and while everything is hanging together so far, the cluster’s performance needs some tuning.

Sending PDF files in the response

To send PDF files so that most browsers will display them I’ve found the following headers work:

  • Content-Disposition: inline; filename=sample.pdf or Content-Disposition: attachment; filename=sample.pdf
  • Expires: 0
  • Cache-Control: must-revalidate, post-check=0, pre-check=0
  • Pragma: public
  • Content-Type: application/pdf
  • Content-Length: <number of bytes>

Here is some rough Java to stream an input stream to the HttpServletResponse.

public static void streamInputToResponse(HttpServletResponse resp, boolean isInline, String contentType, String filename,
		InputStream in, int length) throws IOException {
	ServletOutputStream outstr = null;
	
	try {
		if (length > Integer.MAX_VALUE) {
			throw new IOException("File to large to stream");
		}
		
		outstr = resp.getOutputStream();
		
		// Setting required headers
		resp.setContentLength(length);
		resp.setContentType(contentType);
		String inlineOrAttachment = isInline ? "inline;" : "attachment;";
		resp.setHeader("Content-Disposition", inlineOrAttachment + " filename=" + filename);
		
		// setting some extra response headers to sooth browser issues
		resp.setHeader("Expires", "0");
		resp.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
		resp.setHeader("Pragma", "public");

		IOUtils.copy(in, outstr);
		outstr.flush();
	}
	catch (IOException e) {
		String errorStr = "Error Streaming data to servlet output stream" + ": " + e.getMessage();

		try {
			if (outstr != null) {
				outstr.print(errorStr);
				outstr.flush();
			}
		}
		catch (IOException e1) {
			logger.warn("Error flushing servlet output stream: " + e1.getMessage());
		}
		
		throw e;
	}
	finally {
		IOUtils.closeQuietly(in);
		IOUtils.closeQuietly(outstr);
	}
}

Update:
The Restlet framework has some issues with PDFs when used in JBoss / Tomcat. Tomcat is appending a charset=UTF8 that appears to stop chrome’s built in PDF reader from loading (Chrome 11). Not setting the content length appeared to help while surprisingly not breaking IE.
Also with JBoss and authentication a no-cache header is added and the Restlet framework doesn’t remove it (even if you add your own Cache-Control header). See here for a way to disable this behaviour.

Macbook Pro intermittent black screen

I have an old Macbook Pro (2008) running Leopard that was having intermittent black screens during operation that wouldn’t go away (not ever after a reboot). The external DVI port would still work, and I passed the GPU tests related to the Apple Nvida GPU issues (TS2377).

My fix was to change the screen resolution via an app called cscreen. It’s hard to find the .dmg file, but not too hard. I wrote some apple script that changes the resolution and colour depth, compiled it as an application and put it in my applications directory. This way I could find it in the “dark” with spotlight by pressing command-space then typing my app name and pressing enter. Interestingly enough sometimes just pressing command-space would bring the screen back to life (but nothing else seemed to).

Glad to have a work around, but it still seems a weird issue.

The link to the article that go me on to cscreen is here.

Update:┬áThis solution didn’t last long and the laptop died – I suspect it was the known GPU issue with this model, but Apple denied it (Sydney Apple store) and can’t run the tests as the laptop is dead. Sniff.

Determining GWT Version from jar sizes

I ran into a few cases trying to build a GWT project that does not clearly state which GWT version it uses. AFAICT the gwt-servlet.jar doesn’t give any clues in the manifest or else where.

Here’s a list of versions of gwt-servlet.jar and file sizes (KB):

  • 2.0.0 – 1,524 KB
  • 2.0.1 – 1,535 KB
  • 2.0.2 – 1,538 KB* see below – 1,574,024 Kb,
  • 2.0.3 – 1,558 KB* see above – 1,574,034 Kb
  • 2.0.4 – 1,538 KB
  • 2.1.0 – 2,776 KB
  • 2.1.1 – 2,906 KB
  • 2.2.0 – 4,280 KB
  • 2.3.0 – 4,889 KB

Prior to 2.0.0 there is a separate version for Win/Mac/Linux

Got any others to add?

Selenium pressing or sending End and Home Keys

Quick one: To enumlate the end and home key presses you can use:

selenium.focus("somelocator...");
selenium.keyPress("somelocator...", "35"); // end
selenium.keyPress("somelocator...", "36"); // home

I need this for a GXT 2.2.3 TreePanel that has nodes that are off the screen (overflow) as GXT nodes are not rendered until they are scrolled into view. This workaround renders all the nodes in the tree.

GXT Enums, ComboBoxes and i18n

If you want to have a ComboBox that uses enums with i18n you can use a SimpleComboBox and use the enums toString() method to return the GWT i18n constants.

As I posted on the GXT forums, I don’t want to use toString() as I want the enum to still work on the server side (say during debugging).

Instead I have a getLabel() method and want the GXT ComboBox to call that. Here’s the code:

It does the following:

  • Convert the value in the ComboBox with a property editor to call getLabel()
  • Convert the values in the list with a ModelProcessor to call getLabel() and Template
  • The Enum

    public enum Condition {
      New,
      Used,
      Damaged
    
      public String getLabel()
      {
          // get i18n with a GWT create call save via a ConstantsWithLookup
      }
    }
    

    The ComboBox that uses Condition enum (with a getLabel() method for i18n)

    final SimpleComboBox<Condition> combo = new SimpleComboBox<Condition>();
    combo.setFieldLabel("Condition");
    combo.setName("condition");
    combo.add(Arrays.asList(Condition.values()));        
    combo.setEditable(false);
    combo.setAllowBlank(false);
    combo.setTriggerAction(TriggerAction.ALL);
    combo.setSimpleValue(Condition.NEW);
    

    The standard enum field binding:

    FieldBinding binding = new FieldBinding(combo, "condition") 
    {
        @SuppressWarnings("unchecked")
        @Override
        protected Object onConvertFieldValue(Object value) {
            return ((SimpleComboValue<Condition>)value).getValue();
        }
    
        @Override
        protected Object onConvertModelValue(Object value) {
            return combo.findModel((Condition)value);
        }
    };
    formBinding.addFieldBinding(binding);
    

    The rest (pretty ugly but it works)

    // Replace the text in the box with the enums label
    ListModelPropertyEditor<SimpleComboValue<Condition>> propEditor = 
        new ListModelPropertyEditor<SimpleComboValue<Condition>>()
        {
             public String getStringValue(SimpleComboValue<Condition> value) {
                 return value.getValue().getLabel();
             }
        };
    
    propEditor.setDisplayProperty("label");
    combo.setPropertyEditor(propEditor);
            
    // Replace the text in the list with the enums labels
    combo.getView().setModelProcessor(new ModelProcessor<SimpleComboValue<Condition>>() 
    {
        public SimpleComboValue<Condition> prepareData(SimpleComboValue<Condition> model) 
        {
            model.set("label", model.getValue().getLabel());
            return model;
        }
    });
            
    // Use label property in list as SimpleComboBox displayProperty "value" is used by default
    String html = "<tpl for=\".\"><div role=\"listitem\" class=\"" + combo.getListStyle() + "-item\">{" + "label" + "}</div></tpl>";
    combo.setTemplate(html);
    

    If all used enums implemented a “Labelable” (or Internationalisable?) interface that required the getLabel() method then this code could be reused for different enums.

    I’m using GXT 2.2.3 and GWT 2.0.4.

    Sort HashMap By value

    Recently I evaluated a few methods of sorting a HashMap by value.

    The two main ways I found (based on this thread were:

    Using LinkedLists

    • Handles duplicate values by leaving keys in the same order they are in the source
    • This example passes in a comparator, but if the values are comparable you could use compareTo() instead
    • Performs a once off sort, so you need to re-sort if the map changes
    public static <K, V> Map<K, V> sortByValue(Map<K, V> map, final Comparator<V> valueComparator)
        List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<K, V>>()  {
            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
                return vComparator.compare(o1.getValue(), o2.getValue());
            }
        });
    
        Map<K, V> result = new LinkedHashMap<K, V>();
        for (Map.Entry<K, V> entry : list)  {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    } 
    

    Using a TreeMap

    • Duplicates values will be removed from the tree so you need to add the key to the compare
    • In this example I use a comparator for both the values and the key (if the values are equal)
    • Always keeps the map sorted if the sorted map is used
    Map baseMap = ...          
    KeyComparator kComparator = new KeyComparator();
    ValueComparator vComparator = new ValComparator();
              
    MapValueComparator mapValComparator = new MapValueComparator(baseMap, kComparator, vComparator);
         
    Map sortedMap = new TreeMap(mapValComparator);
    sortedMap.putAll(baseMap);
    

    The map value comparator use above:

    public class MapValueComparator<K, V> implements Comparator<K>
    {
        private Map<K,V> map;
        private Comparator<K> keyComparator;
        private Comparator<V> valueComparator;
        
        public MapValueComparator(Map<K, V> map, Comparator<K> keyComparator, Comparator<V> valueComparator) {
            this.map = map;
            this.valueComparator = valueComparator;
            this.keyComparator = keyComparator;
        }
        
        public int compare(K o1, K o2) {            
            int valueCompare = valueComparator.compare(map.get(o1), map.get(o2));
            if (valueCompare == 0) {
                return keyComparator.compare(o1, o2);
            }
            
            return valueCompare;
        }
    }
    

    Selenium Handling / Testing Downloads with a custom profile

    Selenium and downloads don’t play nicely as the Firefox (et al) download dialogs are out of reach of Selenium. The GWT application I’m working on allows the user to click on an icon and have an Excel file download initiated.

    Searching for ways to test downloading files with Selenium came up with methods focused on running some kind of download dialog GUI interaction tool like AutoIT (anyone tried sikuli?).

    While this is a practical solution it feels fragile to me.

    To avoid this I created a custom Firefox profile to use as a template. This custom profile contains a customised download save path that I use to check files are downloaded, and a mimetype setting to that Excel files are automatically saved.

    To do this:

    • Copy the customProfileDirCUSTFF from the Selenium 1.0.1 sources
    • Add a custom prefs.js file with a download path set (at least)
    • Add a mineTypes.rdf and set excel files to auto save (alwaysAsk=false)
    • Start the selenium server with a template profile parameter

    The easiest way to create these files is by changing the settings in Firefox and then looking at the prefs.js file and mimeTypes.rdf file in your profile directory.

    The line from the prefs.js is:

    user_pref("browser.download.dir", "C:\\temp\\ff_selenium_downloads");
    

    And the section from the mimeTypes:

      <RDF:Description RDF:about="urn:mimetype:handler:application/vnd.ms-excel"
                       NC:saveToDisk="true"
                       NC:alwaysAsk="false" />
    

    To start your Selenium server with the template profile use:

    -firefoxProfileTemplate

    java -jar selenium-server.jar -firefoxProfileTemplate

    or in Java:

        RemoteControlConfiguration rcc = new RemoteControlConfiguration();
        rcc.setPort(SeleniumUtils.SELENIUM_SERVER_PORT);
        rcc.setFirefoxProfileTemplate(customFFProfileDir);
        server = new SeleniumServer(rcc);
        server.start();