Friday, 6 January 2012

Bulk upload of maven artifacts to remote repo

If you have an artifact you want to upload to a remote repository (i.e. internal company repo) then you use the command:
mvn deploy:deploy-file -Durl=file://C:\m2-repo \
                       -DrepositoryId=some.id \
                       -Dfile=your-artifact-1.0.jar \
                       [-DpomFile=your-pom.xml] \


However if you have more than a few files and they're in a nested directory structure (like your local repo) then this is not the way to do it. There doesn't seem to be any tool for either maven or nexus to do this, so here's a quick and dirty way I hacked together:
/**
 * run as java Importer [dirtosearch]
 **/
public class Importer {
   private String directory;
   private final static String repositoryId = "repo1";
   private final static String url = "http://remoteserver/nexus/content/repositories/repo1/";
   private final static String mvn = "C:/java/apache-maven-3.0.3/bin/mvn.bat";

   /**
    * @param directory
    */
   public Importer(String directory) {
      super();
      this.directory = directory;
   }

   public static void main(String[] args) {
      Importer importer = new Importer(args[0]);
      importer.go();
   }

   private void go() {
      try {
         File dir = new File(directory);

         doDir(dir);
      }
      catch (Throwable e) {
         e.printStackTrace();
      }
   }

   private void doDir(File dir) throws IOException, InterruptedException {
      File[] listFiles = dir.listFiles(new PomFilter());
      if (listFiles != null) {
         for (File pom : listFiles) {
            doPom(pom);
         }
      }

      File[] listDirs = dir.listFiles(new DirFilter());
      if (listDirs != null) {
         for (File subdir : listDirs) {
            doDir(subdir);
         }
      }
   }

   private void doPom(File pom) throws IOException, InterruptedException {
      File base = pom.getParentFile();
      String fileName = pom.getName();
      String jarName = fileName.substring(0, fileName.length() - 3) + "jar";
      File jar = new File(base.getAbsolutePath() + "/" + jarName);

      String exec = mvn + " deploy:deploy-file -DrepositoryId=" + repositoryId + " -Durl=" + url;
      if (jar.exists()) {
         exec += " -Dfile=\"" + jar.getAbsolutePath() + "\"";
      }
      else {
         exec += " -Dfile=\"" + pom.getAbsolutePath() + "\"";
      }
      exec += " -DpomFile=\"" + pom.getAbsolutePath() + "\"";
      exec(exec);

   }

   private void exec(String exec) throws InterruptedException, IOException {
      System.out.println(exec);
      Process p = Runtime.getRuntime().exec(exec);
      String line;
      BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream()));
      BufferedReader bre = new BufferedReader(new InputStreamReader(p.getErrorStream()));
      while ((line = bri.readLine()) != null) {
         System.out.println(line);
      }
      bri.close();
      while ((line = bre.readLine()) != null) {
         System.out.println(line);
      }
      bre.close();
      p.waitFor();
      System.out.println("Done.");
   }

   private class PomFilter implements java.io.FileFilter {

      @Override
      public boolean accept(File pathname) {
         return pathname.getName().endsWith(".pom");
      }
   }

   private class DirFilter implements java.io.FileFilter {

      @Override
      public boolean accept(File pathname) {
         return pathname.isDirectory();
      }
   }
}

Wednesday, 11 May 2011

WAS SOAPFault causing throws a WebServicesFault

When returning a SOAPFault from a SOAP handler I saw this in Websphere Application Server's (6.1.0) console:
[11/05/11 18:20:51:654 EST] 00000047 WebServicesSe E com.ibm.ws.webservices.engine.transport.http.WebServicesServlet doPost WSWS3227E:  Error: Exception:
                                 WebServicesFault
 faultCode: 666
 faultString: myfaultstring
 faultActor: myhandler
 faultDetail: 

myfaultstring
 at com.ibm.ws.webservices.engine.xmlsoap.builders.WebServicesFaultProcessor.createFault(WebServicesFaultProcessor.java:420)
 at com.ibm.ws.webservices.engine.xmlsoap.SOAPFault.getFault(SOAPFault.java:615)
 at com.ibm.ws.webservices.engine.SOAPPart.getFault(SOAPPart.java:1097)
 at com.ibm.ws.webservices.engine.SOAPPart.getFault(SOAPPart.java:754)
 at com.ibm.ws.webservices.engine.Message.getFault(Message.java:935)
 ....
Or when throwing a custom SOAPFaultException:
[11/05/11 18:32:05:678 EST] 00000065 WebServicesSe E com.ibm.ws.webservices.engine.transport.http.WebServicesServlet doPost WSWS3227E:  Error: Exception:
                                 WebServicesFault
 faultCode: myWebServiceFunction
 faultString: my test
 faultActor: testActor
 faultDetail: 

my test
 at com.ibm.ws.webservices.engine.WebServicesFault.makeFault(WebServicesFault.java:189)
 at com.ibm.ws.webservices.engine.dispatchers.java.SessionDispatcher.invoke(SessionDispatcher.java:213)
 at com.ibm.ws.webservices.engine.PivotHandlerWrapper.invoke(PivotHandlerWrapper.java:248)
 ...

A SOAP fault is still returned to the client but you get a WebServicesFault exception on both ends. Digging into Websphere's with the help of a decompiler shows that this is caused simply by no fault detail being passed. Simply adding a fault detail with a text of "error" gets rid of this error.

Monday, 1 November 2010

Persisting ViewScoped beans across views in request

I came across this problem when trying to deal with view scoped beans that you want to reuse in a different view, i.e. after clicking next in a wizard like page.

This was originally Discussed Here (now removed since Oracle merged Sun's forums).

The View Scope
There is a a brief summary of it here: JSF 2.0 New Feature Preview Series (Part 5) EDR1 Potpourri.
Basically view scoped values are attached to the current viewId. Values for the view scoped variables are stored in the map returned by UIViewRoot.getViewMap(boolean). Which is great for when your accessing the same view. However, it is cleared when the view is changed (e.g. from a navigation result) FacesContext.setViewRoot(javax.faces.component.UIViewRoot)

However, the flash scope was designed seemingly to solve this very problem.

Using the Flash Scope
Here's a write up of the Understanding the JSF 2.0 Flash scope. There's also some caveats shown here: The benefits and pitfalls of @ViewScoped.
So the logical step is to try it, :
public String doIt() {
   FacesContext.getCurrentInstance().getExternalContext().getFlash().put("thisBean", this);   return "next";
}
However, after some poking around, I discovered you can't re-store in FlashScope as ManagedBeanELResolver is before ScopedAttributeELResolver in the expression language resolver chain. Meaning that if you have a view "view1" with bean name "viewScopeBean1" and want to use the exact same bean in "view2" with bean name "viewScopeBean2", you can't.
The most you can do is store whatever data you need in the flash scope, then in the @PostConstruct of the new bean, pull in the values you need. Not an elegant solution but I'm sure messing around with the ELResolver chain could also do it but then you run the risk of it breaking else where.

Tuesday, 12 October 2010

f:convertNumber parses to double not BigDecimal

The default behaviour of the f:convertNumber, implemented with NumberConverter, in converting a decimal String of "1.11", is to convert it to a double and then JSF converts that to a BigDecimal. Most of the time you won't notice this happening until you use CallableStatement.setBigDecimal to invoke a stored procedure. In MS SQL Server, you get the following error:
com.microsoft.sqlserver.jdbc.SQLServerException: Error converting data type nvarchar to decimal.
The cause of this is because inside the BigDecimal it stored "1100000000000000976996261670137755572795867919921875" as a result of converting the String to a double. This floating point error is the motivation for using BigDecimal in the first place. And of course because the SQL Server JDBC driver didn't know how to handle the BigDecimal properly.

Looking into ConvertNumber, it uses one of two converters (DecimalFormat or NumberFormat) depending on whether 'pattern' is used. In Java 5, they added setParseBigDecimal(boolean newValue) in order to yield the desired output.

You can't easily change the NumberConverter but you can very simply add your own. To do so, I grabbed the source of NumberConverter from Mojarra 2.0.2 and copied all of it into my own class: net.devgrok.BigDecimalConverter.
@FacesConverter(forClass = BigDecimal.class, value="bigdecimal")
public class BigDecimalConverter implements Converter, PartialStateHolder {

// ... existing code

  public Object getAsObject(FacesContext context, UIComponent component, String value) {

// ... existing code

      // Create and configure the parser to be used
      parser = getNumberFormat(locale);
      if (((pattern != null) && pattern.length() != 0) || "currency".equals(type))
      {
        configureCurrency(parser);
      }
      parser.setParseIntegerOnly(isIntegerOnly());
      boolean groupSepChanged = false;

      // BEGIN BigDecimal HACK
      if (parser instanceof DecimalFormat)
      {
        ((DecimalFormat) parser).setParseBigDecimal(true);
      }
      // END BigDecimal HACK
      
// ... existing code

  }

// ... existing code

}
Then just create a file META-INF/custom.taglib.xml:
<?xml version="1.0" encoding="UTF-8"?>

<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
    version="2.0">

    <namespace>http://devgrok.net/facestags</namespace>

    <tag>
        <tag-name>convertBigDecimal</tag-name>
        <converter>
            <converter-id>bigdecimal</converter-id>
        </converter>
    </tag>

</facelet-taglib>

And to use:
<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:fn="http://java.sun.com/jsp/jstl/functions"
  xmlns:c="http://java.sun.com/jsp/jstl/core"
  xmlns:dg="http://devgrok.net/facestags"
  >

  from goes here etc
  
  <h:inputText id="txtPrice" value="#{bean.item.price}">
    <dg:convertBigDecimal pattern="##0.00" />
  </h:inputText>
  
</ui:composition

Sunday, 19 September 2010

Fixing my HTC Desire's wifi

I recently did an Over The Air (OTA) update of my new HTC Desire from 2.09 to 2.10. The update all seemed to run correctly. However I soon noticed that the wifi wasn't working. Looking in settings, it showed wifi was disabled and when you try and enable it, you just get "error". Seems like other people have been getting this as well. I tried following their advice, reset the factory defaults but that didn't work. I couldn't get the recovery mode to load (in order to wipe the caches) - after some brief searching it appeared that this was due to root access so I gave up on that.
NOTE: it turns out I was doing it wrong. After selecting the recovery option a red triangle with an exclamation point is show. At which point you need to press volume+ and power to show the next menu.

Next I tried using latest RUU (2.10) but got Error 170 Usb Connection Error. One post mentions under 'update':
HTC has released a new Froyo OTA update. You can install this directly as an RUU. You may need the latest version of HTC Sync for this version. There appear to be a lot of issues installing this RUU due to drivers. I'd recommend you install the earlier version and update OTA to this version (you can do this manually too).

Using 1.21.405.2 gave me ERROR [131] customer id error. After some poking around, turns out that branded phones don't have a CID... That's what you get with a phone from Hong Kong I guess. However I'm not really sure if that's true...

Eventually I came across this thread which mentions fixing the wifi error with this update: Froyo (2.09.405.8) update - Windows not required. It all installed correctly and after starting it up, it ran through the first-time setup and this time said it detected a wireless network. Success! I didn't try the windows RUU for 2.09 so I'm not sure if that will work.

Next I downloaded the update manually as per the instructions here. The phone started downloading OTA_Bravo_HTC_EU_2.10.405.2-2.09.405.8_release567cypyi0feo4th5.zip so I'm using that instead of the one in the post.

This thread has a better discussion of the problem, with some simple instructions on how to revert and reload when a goldcard isn't needed.