Monday, November 26, 2012

PhoneGAP without Javascript! My demo of GWT 2.5 at the JoziJUG


Live code demos are scary

I did a live code demo to a bunch of Java geeks at the JoziJUG event

1) The showcase application I demoed on a iPad3, Android 2.1 phone, Android 3 tab, Android 4 phone, Windows 2008 tab last night is downloadable from PhoneGap Build. 
https://build.phonegap.com/apps/249950/share
The plain ugly Hello World HTML version is also downloadable :https://build.phonegap.com/apps/249918/share
2) You can also give GWT 2.5 a spin , even if it's in Eclipse (Marius and Myron ) ;-) 
http://mojo.codehaus.org/gwt-maven-plugin/user-guide/archetype.html
3) You can also check out the fancy demo I hacked together using m-gwt and gwt-phonegap 
https://github.com/rjdkolb/jozijug_phonegapdemo
You will need to read Google's MVP to understand the concepts :https://developers.google.com/web-toolkit/doc/latest/DevGuideMvpActivitiesAndPlaces

To the real person who made this possible in GWT :
Daniel Kurka thank you!

To Google, GWT is evolving so nicely :)
Here is Google's closure-compiler that is now built into GWT 2.5
Java is so cool! 
Richard.

Below is me pretending to know something about Java :)


Wednesday, October 31, 2012

Java Community Process EC Elections 2012

The election results are out!


Here is how the Jozi JUG voted :



Most notably on the open seat side, here are the people who got elected.

London Java Community 164 votes (33%)

CloudBees 101 votes (20%)


Thanks to everyone who made the effort to vote.


More info here.

Thursday, October 11, 2012

Help Getting a GWT error : com.google.gwt.dev.jjs.InternalCompilerException: Unexpected error during visit.

GWT can be painful sometimes

I was getting the following error while using GWT 2.4.0, GWT 2.5.0 and 2.5.1


[gwt:compile]
auto discovered modules [za.co.enerweb.ies.iesmobile]
Compiling module za.co.enerweb.ies.iesmobile
   Validating units:
      Ignored 1 unit with compilation errors in first pass.
Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.
   [ERROR] An internal compiler exception occurred
com.google.gwt.dev.jjs.InternalCompilerException: Unexpected error during visit.
 at com.google.gwt.dev.jjs.ast.JVisitor.translateException(JVisitor.java:109)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:276)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
 at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:116)
 at com.google.gwt.dev.jjs.ast.JCastOperation.traverse(JCastOperation.java:65)
 at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
 at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:116)
 at com.google.gwt.dev.jjs.ast.JDeclarationStatement.traverse(JDeclarationStatement.java:48)
 at com.google.gwt.dev.jjs.ast.JModVisitor$ListContextImmutable.traverse(JModVisitor.java:170)
 at com.google.gwt.dev.jjs.ast.JModVisitor.acceptWithInsertRemoveImmutable(JModVisitor.java:336)
 at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:83)
 at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
 at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:137)
 at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:133)
 at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:82)
 at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
 at com.google.gwt.dev.jjs.ast.JMethod.visitChildren(JMethod.java:426)
 at com.google.gwt.dev.jjs.ast.JMethod.traverse(JMethod.java:395)
 at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:265)
 at com.google.gwt.dev.jjs.impl.UnifyAst.mainLoop(UnifyAst.java:902)
 at com.google.gwt.dev.jjs.impl.UnifyAst.exec(UnifyAst.java:627)
 at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.precompile(JavaToJavaScriptCompiler.java:604)
 at com.google.gwt.dev.jjs.JavaScriptCompiler.precompile(JavaScriptCompiler.java:33)
 at com.google.gwt.dev.Precompile.precompile(Precompile.java:278)
 at com.google.gwt.dev.Precompile.precompile(Precompile.java:229)
 at com.google.gwt.dev.Precompile.precompile(Precompile.java:141)
 at com.google.gwt.dev.Compiler.run(Compiler.java:232)
 at com.google.gwt.dev.Compiler.run(Compiler.java:198)
 at com.google.gwt.dev.Compiler$1.run(Compiler.java:170)
 at com.google.gwt.dev.CompileTaskRunner.doRun(CompileTaskRunner.java:88)
 at com.google.gwt.dev.CompileTaskRunner.runWithAppropriateLogger(CompileTaskRunner.java:82)
 at com.google.gwt.dev.Compiler.main(Compiler.java:177)
Caused by: java.lang.AbstractMethodError: org.apache.xerces.dom.ElementNSImpl.setUserData(Ljava/lang/String;Ljava/lang/Object;Lorg/w3c/dom/UserDataHandler;)Ljava/lang/Object;
 at com.google.gwt.uibinder.rebind.W3cDocumentBuilder.startElement(W3cDocumentBuilder.java:127)
 at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
 at org.apache.xerces.impl.dtd.XMLDTDValidator.startElement(Unknown Source)
 at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
 at org.apache.xerces.impl.XMLNSDocumentScannerImpl$NSContentDispatcher.scanRootElementHook(Unknown Source)
 at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
 at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
 at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
 at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
 at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
 at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
 at javax.xml.parsers.SAXParser.parse(SAXParser.java:392)
 at com.google.gwt.uibinder.rebind.W3cDomHelper.documentFor(W3cDomHelper.java:74)
 at com.google.gwt.uibinder.rebind.UiBinderGenerator.getW3cDoc(UiBinderGenerator.java:204)
 at com.google.gwt.uibinder.rebind.UiBinderGenerator.generateOnce(UiBinderGenerator.java:174)
 at com.google.gwt.uibinder.rebind.UiBinderGenerator.generate(UiBinderGenerator.java:129)
 at com.google.gwt.core.ext.IncrementalGenerator.generateNonIncrementally(IncrementalGenerator.java:40)
 at com.google.gwt.dev.javac.StandardGeneratorContext.runGeneratorIncrementally(StandardGeneratorContext.java:657)
 at com.google.gwt.dev.cfg.RuleGenerateWith.realize(RuleGenerateWith.java:41)
 at com.google.gwt.dev.shell.StandardRebindOracle$Rebinder.rebind(StandardRebindOracle.java:79)
 at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:276)
 at com.google.gwt.dev.shell.StandardRebindOracle.rebind(StandardRebindOracle.java:265)
 at com.google.gwt.dev.DistillerRebindPermutationOracle.getAllPossibleRebindAnswers(DistillerRebindPermutationOracle.java:91)
 at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.handleGwtCreate(UnifyAst.java:355)
 at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.handleMagicMethodCall(UnifyAst.java:433)
 at com.google.gwt.dev.jjs.impl.UnifyAst$UnifyVisitor.endVisit(UnifyAst.java:237)
 at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:243)
 at com.google.gwt.dev.jjs.ast.JModVisitor.traverse(JModVisitor.java:361)
 at com.google.gwt.dev.jjs.ast.JModVisitor.accept(JModVisitor.java:273)
 ... 37 more
      [ERROR] at MainPage.java(27): GWT.create(MainPage$MainPageUiBinder.class)
         com.google.gwt.dev.jjs.ast.JMethodCall
      [ERROR] at MainPage.java(27): (MainPage$MainPageUiBinder) GWT.create(MainPage$MainPageUiBinder.class)
         com.google.gwt.dev.jjs.ast.JCastOperation
      [ERROR] at MainPage.java(27): static MainPage$MainPageUiBinder uiBinder = (MainPage$MainPageUiBinder) GWT.create(MainPage$MainPageUiBinder.class)
         com.google.gwt.dev.jjs.ast.JDeclarationStatement
      [ERROR] at MainPage.java(23): {
  Page.$clinit();
  static MainPage$MainPageUiBinder uiBinder = (MainPage$MainPageUiBinder) GWT.create(MainPage$MainPageUiBinder.class);
}
         com.google.gwt.dev.jjs.ast.JBlock
      [ERROR] at MainPage.java(23): {
  Page.$clinit();
  static MainPage$MainPageUiBinder uiBinder = (MainPage$MainPageUiBinder) GWT.create(MainPage$MainPageUiBinder.class);
}
         com.google.gwt.dev.jjs.ast.JMethodBody
      [ERROR] at MainPage.java(23): private static final void $clinit();

         com.google.gwt.dev.jjs.ast.JMethod 
 
The UI Binder Java code
public class MainPage extends Page {

    @UiField
    ListPanel list;
    private static MainPageUiBinder uiBinder = GWT.create(MainPageUiBinder.class);

    interface MainPageUiBinder extends UiBinder {
    }

    public MainPage() {
        initWidget(uiBinder.createAndBindUi(this));

 
 

The solution

 There was a very old version old version of xercesImpl 2.6.2 included by Jersey
       
        
            com.sun.jersey.contribs
            jersey-atom-abdera
            1.8
        

So find it and banish it to the place it came from and don't forget to clean package
 
          
            com.sun.jersey.contribs
            jersey-atom-abdera
            1.8
          
            
              xercesImpl
              xerces
            
          
        
       
 
 
 

Friday, August 31, 2012

Et tu, Richard. I stabbed myself in the back with Vaadin and Lombok

The problem I struggled with for days... 

Take a simple Vaadin tab sheet.
Add a sheet , remove a sheet etc. Nothing hard at all right ?

I was getting the below error when the screen was repainted.
 
SEVERE: Terminal error:
java.lang.NullPointerException
 at com.vaadin.ui.TabSheet.paintContent(TabSheet.java:390)
 at com.vaadin.ui.AbstractComponent.paint(AbstractComponent.java:781)
 at com.vaadin.ui.AbstractSplitPanel.paintContent(AbstractSplitPanel.java:235)
 at com.vaadin.ui.AbstractComponent.paint(AbstractComponent.java:781)
 at com.vaadin.ui.AbstractOrderedLayout.paintContent(AbstractOrderedLayout.java:187)
 at com.vaadin.ui.AbstractComponent.paint(AbstractComponent.java:781)
 at com.vaadin.ui.Panel.paintContent(Panel.java:269)
 at com.vaadin.ui.Window.paintContent(Window.java:641)
 at com.vaadin.ui.AbstractComponent.paint(AbstractComponent.java:781)
 at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.writeUidlResponce(AbstractCommunicationManager.java:1046)
 at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.paintAfterVariableChanges(AbstractCommunicationManager.java:927)
 at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:794)
 at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:296)
 at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:501)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
 at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:169)
 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
 at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
 at org.apache.catalina.core.StandardHostValve.__invoke(StandardHostValve.java:168)
 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java)
 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
 at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
 at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:964)
 at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
 at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:304)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
 at java.lang.Thread.run(Thread.java:722)


There seems to be a Vaadin ticket for this : http://dev.vaadin.com/ticket/4179  

The culprit

My favorite java Add On : Lombok
Actually I can't blame Lombok or Vaadin, this is my misuse of Lombok inside Vaadin

I decorated my component with @Data
That means the equals and hashCode were generated by all the fields in the class.

com.vaadin.ui.TabSheet.java contains an internal representation of the tabs in a HashMap tabs.

If the hashmap can not find your component because the equals has changed. you will get what I did.


            Tab tab = tabs.get(component);//will return null

            target.startTag("tab");
            if (!tab.isEnabled() && tab.isVisible()) { 
 
 
 

Monday, August 13, 2012

Expanding on old LDAPManager.java

In 2001 Brett McLaughlin published a small class with no dependencies called LDAPManager.java.
It's Super simple, as apposed to many libraries that try to make it very complex.


I have added three methods :
// Snip
    public List getUsersString() throws NamingException {}
    public List getUsers() throws NamingException {}
    public List getGroups() throws NamingException {}


// Snip

/*

 Copyright (C) 2001 Brett McLaughlin.
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 1. Redistributions of source code must retain the above copyright
 notice, this list of conditions, and the following disclaimer.

 2. Redistributions in binary form must reproduce the above copyright
 notice, this list of conditions, and the disclaimer that follows
 these conditions in the do*****entation and/or other materials
 provided with the distribution.

 3. The name "Building Java Enterprise Applications" must not be used
 to endorse or promote products derived from this software without
 prior written permission.  For written permission, please contact
 brett@newInstance.com.

 In addition, we request (but do not require) that you include in the
 end-user do*****entation provided with the redistribution and/or in the
 software itself an acknowledgement equivalent to the following:
 "This product includes software developed for the
 'Building Java Enterprise Applications' book, by Brett McLaughlin
 (O'Reilly & Associates)."

 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED.  IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 SUCH DAMAGE.

 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.NameClassPair;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.AttributeInUseException;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.NoSuchAttributeException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

public class LDAPManager {

    /**
     * The OU (organizational unit) to add users to
     */
    private  String USERS_OU =
            "ou=People,ou=People,dc=example,dc=com";
    /**
     * The OU (organizational unit) to add groups to
     */
    private String GROUPS_OU =
            "o=groups,ou=People,dc=example,dc=com";
    /**
     * The OU (organizational unit) to add permissions to
     */
    private String PERMISSIONS_OU =
            "ou=Permissions,o=forethought.com";
    /**
     * The default LDAP port
     */
    private static final int DEFAULT_PORT = 389;
    /**
     * The LDAPManager instance object
     */
    private static Map instances = new HashMap();
    /**
     * The connection, through a
     * DirContext, to LDAP
     */
    private DirContext context;
    /**
     * The hostname connected to
     */
    private String hostname;
    /**
     * The port connected to
     */
    private int port;

    protected LDAPManager(String hostname, int port,
            String username, String password,String USERS_OU,String GROUPS_OU,String PERMISSIONS_OU)
            throws NamingException {
        this.USERS_OU = USERS_OU;
        this.GROUPS_OU = GROUPS_OU;
        this.PERMISSIONS_OU = PERMISSIONS_OU;

        context = getInitialContext(hostname, port, username, password);

        // Only save data if we got connected
        this.hostname = hostname;
        this.port = port;
    }

    public static LDAPManager getInstance(String hostname,
            int port,
            String username,
            String password,String USERS_OU,String GROUPS_OU,String PERMISSIONS_OU)
            throws NamingException {

        // Construct the key for the supplied information
        String key = new StringBuffer()
                .append(hostname)
                .append(":")
                .append(port)
                .append("|")
                .append((username == null ? "" : username))
                .append("|")
                .append((password == null ? "" : password))
                .toString();

        if (!instances.containsKey(key)) {
            synchronized (LDAPManager.class) {
                if (!instances.containsKey(key)) {
                    LDAPManager instance =
                            new LDAPManager(hostname, port,
                            username, password,USERS_OU, GROUPS_OU, PERMISSIONS_OU);
                    instances.put(key, instance);
                    return instance;
                }
            }
        }

        return (LDAPManager) instances.get(key);
    }

    public static LDAPManager getInstance(String hostname, int port,String USERS_OU,String GROUPS_OU,String PERMISSIONS_OU)
            throws NamingException {

        return getInstance(hostname, port, null, null,USERS_OU, GROUPS_OU, PERMISSIONS_OU);
    }

    public static LDAPManager getInstance(String hostname,String USERS_OU,String GROUPS_OU,String PERMISSIONS_OU)
            throws NamingException {

        return getInstance(hostname, DEFAULT_PORT, null, null,USERS_OU, GROUPS_OU, PERMISSIONS_OU);
    }

    public void addUser(String username, String firstName,
            String lastName, String password)
            throws NamingException {

        // Create a container set of attributes
        Attributes container = new BasicAttributes();

        // Create the objectclass to add
        Attribute objClasses = new BasicAttribute("objectClass");
        objClasses.add("top");
        objClasses.add("person");
        objClasses.add("organizationalPerson");
        objClasses.add("inetOrgPerson");

        // Assign the username, first name, and last name
        String cnValue = new StringBuffer(firstName)
                .append(" ")
                .append(lastName)
                .toString();
        Attribute cn = new BasicAttribute("cn", cnValue);
        Attribute givenName = new BasicAttribute("givenName", firstName);
        Attribute sn = new BasicAttribute("sn", lastName);
        Attribute uid = new BasicAttribute("uid", username);

        // Add password
        Attribute userPassword =
                new BasicAttribute("userpassword", password);

        // Add these to the container
        container.put(objClasses);
        container.put(cn);
        container.put(sn);
        container.put(givenName);
        container.put(uid);
        container.put(userPassword);

        // Create the entry
        context.createSubcontext(getUserDN(username), container);
    }

    public void deleteUser(String username) throws NamingException {
        try {
            context.destroySubcontext(getUserDN(username));
        } catch (NameNotFoundException e) {
            // If the user is not found, ignore the error
        }
    }

    public boolean isValidUser(String username, String password)
            throws UserNotFoundException {
        try {
            DirContext context =
                    getInitialContext(hostname, port, getUserDN(username),
                    password);
            return true;
        } catch (javax.naming.NameNotFoundException e) {
            throw new UserNotFoundException(username);
        } catch (NamingException e) {
            // Any other error indicates couldn't log user in
            return false;
        }
    }

    public void addGroup(String name, String description)
            throws NamingException {

        // Create a container set of attributes
        Attributes container = new BasicAttributes();

        // Create the objectclass to add
        Attribute objClasses = new BasicAttribute("objectClass");
        objClasses.add("top");
        objClasses.add("groupOfUniqueNames");
        objClasses.add("groupOfForethoughtNames");

        // Assign the name and description to the group
        Attribute cn = new BasicAttribute("cn", name);
        Attribute desc = new BasicAttribute("description", description);

        // Add these to the container
        container.put(objClasses);
        container.put(cn);
        container.put(desc);

        // Create the entry
        context.createSubcontext(getGroupDN(name), container);
    }

    public void deleteGroup(String name) throws NamingException {
        try {
            context.destroySubcontext(getGroupDN(name));
        } catch (NameNotFoundException e) {
            // If the group is not found, ignore the error
        }
    }

    public void addPermission(String name, String description)
            throws NamingException {

        // Create a container set of attributes
        Attributes container = new BasicAttributes();

        // Create the objectclass to add
        Attribute objClasses = new BasicAttribute("objectClass");
        objClasses.add("top");
        objClasses.add("forethoughtPermission");

        // Assign the name and description to the group
        Attribute cn = new BasicAttribute("cn", name);
        Attribute desc = new BasicAttribute("description", description);

        // Add these to the container
        container.put(objClasses);
        container.put(cn);
        container.put(desc);

        // Create the entry
        context.createSubcontext(getPermissionDN(name), container);
    }

    public void deletePermission(String name) throws NamingException {
        try {
            context.destroySubcontext(getPermissionDN(name));
        } catch (NameNotFoundException e) {
            // If the permission is not found, ignore the error
        }
    }

    public boolean assignUser(String username, String groupName)
            throws NamingException {

        try {
            ModificationItem[] mods = new ModificationItem[1];

            Attribute mod =
                    new BasicAttribute("uniqueMember",
                    getUserDN(username));
            mods[0] =
                    new ModificationItem(DirContext.ADD_ATTRIBUTE, mod);
            context.modifyAttributes(getGroupDN(groupName), mods);
            return true;
        } catch (AttributeInUseException e) {
            // If user is already added, ignore exception
            return false;
        }
    }

    public boolean removeUser(String username, String groupName)
            throws NamingException {

        try {
            ModificationItem[] mods = new ModificationItem[1];

            Attribute mod =
                    new BasicAttribute("uniqueMember",
                    getUserDN(username));
            mods[0] =
                    new ModificationItem(DirContext.REMOVE_ATTRIBUTE, mod);
            context.modifyAttributes(getGroupDN(groupName), mods);
            return true;
        } catch (NoSuchAttributeException e) {
            // If user is not assigned, ignore the error
            return false;
        }
    }

    public boolean userInGroup(String username, String groupName)
            throws NamingException {

        // Set up attributes to search for
        String[] searchAttributes = new String[1];
        searchAttributes[0] = "uniqueMember";

        Attributes attributes =
                context.getAttributes(getGroupDN(groupName),
                searchAttributes);
        if (attributes != null) {
            Attribute memberAtts = attributes.get("uniqueMember");
            if (memberAtts != null) {
                for (NamingEnumeration vals = memberAtts.getAll();
                        vals.hasMoreElements();) {
                    if (username.equalsIgnoreCase(
                            getUserUID((String) vals.nextElement()))) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    public List getMembers(String groupName) throws NamingException {
        List members = new LinkedList();

        // Set up attributes to search for
        String[] searchAttributes = new String[1];
        searchAttributes[0] = "uniqueMember";

        Attributes attributes =
                context.getAttributes(getGroupDN(groupName),
                searchAttributes);
        if (attributes != null) {
            Attribute memberAtts = attributes.get("uniqueMember");
            if (memberAtts != null) {
                for (NamingEnumeration vals = memberAtts.getAll();
                        vals.hasMoreElements();
                        members.add(
                                getUserUID((String) vals.nextElement()))) ;
            }
        }

        return members;
    }

    public List getGroups(String username) throws NamingException {
        List groups = new LinkedList();

        // Set up criteria to search on
        String filter = new StringBuffer()
            .append("(&")
            .append("(objectClass=groupOfForethoughtNames)")
            .append("(uniqueMember=")
            .append(getUserDN(username))
            .append(")")
            .append(")")
            .toString();

        // Set up search constraints
        SearchControls cons = new SearchControls();
        cons.setSearchScope(SearchControls.ONELEVEL_SCOPE);

        NamingEnumeration results =
            context.search(GROUPS_OU, filter, cons);

        while (results.hasMore()) {
            SearchResult result = (SearchResult)results.next();
            groups.add(getGroupCN(result.getName()));
        }

        return groups;
    }

    private NamingEnumeration search(String base) throws NamingException {
        SearchControls cons = new SearchControls();
        cons.setSearchScope(SearchControls.SUBTREE_SCOPE);

        NamingEnumeration results =
                context.list(base);
        return results;
    }

    public List getGroups() throws NamingException {

        ArrayList result = new ArrayList();

        NamingEnumeration items = search(GROUPS_OU);

        while (items != null && items.hasMore()) {

            NameClassPair ctx = (NameClassPair) items.next();
            System.out.println(ctx);
            Attributes attrs = context.getAttributes(ctx.getNameInNamespace());



            String strippedName = this.getUserUID(ctx.getName());
            result.add(strippedName);
        }

        return result;

    }
    public List getUsersString() throws NamingException {
        List usersString = new ArrayList();
        List users = getUsers();
        for (User user : users){
            usersString.add(user.getStrippedDn());
        }
        return usersString;
    }

    public List getUsers() throws NamingException {

        ArrayList result = new ArrayList();

        NamingEnumeration items = search(USERS_OU);

        while (items != null && items.hasMore()) {

            NameClassPair ctx = (NameClassPair) items.next();
            System.out.println(ctx);
            Attributes attrs = context.getAttributes(ctx.getNameInNamespace());


            String cn = "";
            String email = "";
            String givenName = "";

            Attribute tmp = attrs.get("cn");
            if (tmp != null) {
                cn = tmp.toString();
            }

            tmp = attrs.get("givenName");
            if (tmp != null) {
                givenName = tmp.toString();
            }
            tmp = attrs.get("mail");
            if (tmp != null) {
                email = tmp.toString();
            }

            String strippedName = this.getUserUID(ctx.getName());
            result.add(new User(strippedName, ctx.getName(), cn, email, givenName, false));
        }

        return result;

    }

    public void assignPermission(String groupName, String permissionName)
            throws NamingException {

        try {
            ModificationItem[] mods = new ModificationItem[1];

            Attribute mod =
                    new BasicAttribute("uniquePermission",
                    getPermissionDN(permissionName));
            mods[0] =
                    new ModificationItem(DirContext.ADD_ATTRIBUTE, mod);
            context.modifyAttributes(getGroupDN(groupName), mods);
        } catch (AttributeInUseException e) {
            // Ignore the attribute if it is already assigned
        }
    }

    public void revokePermission(String groupName, String permissionName)
            throws NamingException {

        try {
            ModificationItem[] mods = new ModificationItem[1];

            Attribute mod =
                    new BasicAttribute("uniquePermission",
                    getPermissionDN(permissionName));
            mods[0] =
                    new ModificationItem(DirContext.REMOVE_ATTRIBUTE, mod);
            context.modifyAttributes(getGroupDN(groupName), mods);
        } catch (NoSuchAttributeException e) {
            // Ignore errors if the attribute doesn't exist
        }
    }

    public boolean hasPermission(String groupName, String permissionName)
            throws NamingException {

        // Set up attributes to search for
        String[] searchAttributes = new String[1];
        searchAttributes[0] = "uniquePermission";

        Attributes attributes =
                context.getAttributes(getGroupDN(groupName),
                searchAttributes);
        if (attributes != null) {
            Attribute permAtts = attributes.get("uniquePermission");
            if (permAtts != null) {
                for (NamingEnumeration vals = permAtts.getAll();
                        vals.hasMoreElements();) {
                    if (permissionName.equalsIgnoreCase(
                            getPermissionCN((String) vals.nextElement()))) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    public List getPermissions(String groupName) throws NamingException {
        List permissions = new LinkedList();

        // Set up attributes to search for
        String[] searchAttributes = new String[1];
        searchAttributes[0] = "uniquePermission";

        Attributes attributes =
                context.getAttributes(getGroupDN(groupName),
                searchAttributes);
        if (attributes != null) {
            Attribute permAtts = attributes.get("uniquePermission");
            if (permAtts != null) {
                for (NamingEnumeration vals = permAtts.getAll();
                        vals.hasMoreElements();
                        permissions.add(
                                getPermissionCN((String) vals.nextElement()))) ;
            }
        }

        return permissions;
    }

    private String getUserDN(String username) {
        return new StringBuffer()
                .append("cn=")
                .append(username)
                .append(",")
                .append(USERS_OU)
                .toString();
    }

    private String getUserUID(String userDN) {
        int start = userDN.indexOf("=");
        int end = userDN.indexOf(",");

        if (end == -1) {
            end = userDN.length();
        }

        return userDN.substring(start + 1, end);
    }

    private String getGroupDN(String name) {
        return new StringBuffer()
                .append("cn=")
                .append(name)
                .append(",")
                .append(GROUPS_OU)
                .toString();
    }

    private String getGroupCN(String groupDN) {
        int start = groupDN.indexOf("=");
        int end = groupDN.indexOf(",");

        if (end == -1) {
            end = groupDN.length();
        }

        return groupDN.substring(start + 1, end);
    }

    private String getPermissionDN(String name) {
        return new StringBuffer()
                .append("cn=")
                .append(name)
                .append(",")
                .append(PERMISSIONS_OU)
                .toString();
    }

    private String getPermissionCN(String permissionDN) {
        int start = permissionDN.indexOf("=");
        int end = permissionDN.indexOf(",");

        if (end == -1) {
            end = permissionDN.length();
        }

        return permissionDN.substring(start + 1, end);
    }

    private DirContext getInitialContext(String hostname, int port,
            String username, String password)
            throws NamingException {

        String providerURL =
                new StringBuffer("ldap://")
                .append(hostname)
                .append(":")
                .append(port)
                .toString();

        Properties props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        props.put(Context.PROVIDER_URL, providerURL);

        if ((username != null) && (!username.equals(""))) {
            props.put(Context.SECURITY_AUTHENTICATION, "simple");
            props.put(Context.SECURITY_PRINCIPAL, username);
            props.put(Context.SECURITY_CREDENTIALS,
                    ((password == null) ? "" : password));
        }

        return new InitialDirContext(props);
    }
    //added from http://onjava.com/pub/a/onjava/excerpt/bldgjavaent_8/index2.html?page=3

    public boolean updatePassword(String username,
            String oldPassword, String newPassword)
            throws UserNotFoundException {

        // Ensure this is a valid user, with 
        // a valid (old) password
        boolean isValidUser = isValidUser(username,
                oldPassword);
        if (!isValidUser) {
            return false;
        }

        try {
            // Get the user
            DirContext userContext =
                    (DirContext) context.lookup(getUserDN(username));

            ModificationItem[] mods = new ModificationItem[1];

            // Create new password attribute
            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
                    new BasicAttribute("userPassword", newPassword));

            // Replace old with new
            userContext.modifyAttributes("", mods);

            return true;
        } catch (NamingException e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public void close(){
        if (context != null){
            try {
                context.close();
            } catch (NamingException ex) {
                //
            }
            context = null;
        }
    }
}


/*
 *   Building Java Enterprise Applications
 *   By Brett McLaughlin
 *   First Edition March 2002
 *   ISBN: 0-596-00123-1
 *   http://www.oreilly.com/catalog/javentappsv1/
 */
// Snip
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data // Lombok generates all the getters and setters , hashCode toString
//If you don't want that just use your IDE to generate it :)
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String strippedDn;
    private String fullDn;
    private String cn = "";
    private String email = "";
    private String givenName = "";
    transient private boolean groupLess;

}

Friday, July 27, 2012

Java 7, one year later

Java 7, one year later

Java 7 got released on the 28'th of July 2011
The world wide launch party was on the 7'th of July at the JUG with Alexis Moussine-Pouchkine: http://2011.geecon.org/speakerdetails/12

I had the privilege of presenting at the Jozi JUG on the 29'th of August

JRebel claims the adoption is now at 23%
Jelastic claimed 18% in the same article.

Within Jelastic's cloud service: Java 7 is at a massive 73% vs Java 6 at 27%
graph
(Thanks Judah)

Here is the git location of the source code I demoed way back.

git clone https://bitbucket.org/rjdkolb/java7jozijug.git
 
Or on the web : https://bitbucket.org/rjdkolb/java7jozijug/src 


Wednesday, July 25, 2012

Improving the performance of a Vaadin OpenLayers Map

The problem 


I am using Vaadin with it's openlayers addon
I am showing a map with about 10000 points and lines between them.
Vaadin started getting very sluglish

The Solution

Kiren Pillay suggested that I use JProfiler
So that is what I did.
I finally saw what in Vaadin was taking all the time.
getApplication() on the Panel was being called more than 50,000 times
isEnabled() was also being called on the same panel about the same amount of time.

These calls were very fast, but because of the shear number of requests, it was slowing my application down to a crawl.

So with the combination of  JProfiler and JRebel I could apply these changes without a restart!


// Snip
  
   Application app;
   public MyPanel( Application app){
       this.app = app;
   }

   @Override
    public boolean isEnabled(){
        return true;
    }

   @Override
    public Application getApplication(){
        return app;
    } 
 

The end result ?

A dramatic improvement in handing in the browser!


Tuesday, July 24, 2012

Your Java IDE, friend or foe ?

Your Java IDE, friend or foe ?


The new Eclipse Juno is released.
And surprise surprise, it's slow, and caused a couple of sleepless night for a friend of mine.

My advice to any new or experienced Java developer, try NetBeans 7.2.

I know Elcipse has had some of these features for ages, but
1) are they stable ?
2) Do you fear updating your ide ?
3) Is emotional attachment the biggest factor in your choice ?

Here are two videos. Copyright to Netbeans.




Original Netbeans 7.2 Overview







Original Netbeans TestNG , conditional breakpoints video



Monday, July 23, 2012

Production grade REST services


Writing a REST service in Java is easy, writing a production grade one is almost as easy :)

A simple service

// JSR 311 REST service
@Path("myservice")
public class MyDataLoaderREST {

    @PUT
    @Consumes({"application/json"})
    public void loadFile(String csvdata) {
      CSVReader reader = new CSVReader(new StringReader(data));
      String nextLine = reader.readNext();
      Customer c = new Customer( nextLine[0],nextLine[1]);
      em.persist(c);
    }

}

The above service will work, but it is not really production grade in my opinion.

1) It does not return a positive or negative value
2) If something goes wrong like the csv is malformed, it will be next to impossible to debug.

The solution simple service

1) Always return a result, never return a HTTP error, the end user will never be able to figure out what is wrong.
2) If something obvious and something not obvious go wrong, return the line where the error happened.
3) Also don't treat all exceptions in the same way. Catch a FileNotFoundException and a IOException, you may want to handle differently.
4) Catch the RuntimeException as a final resort
5) Write using TDD!

The test!

// A simple JUnit test for testing the import

public class TestMyDataLoaderREST {

    @Test

    public void testImportCustomer() throws IOException {

        MyDataLoaderREST importFile = new MyDataLoaderREST();

        NodeCalculationLoadResult calcResult = importFile.loadFile("1,2");

        Assert.assertTrue(calcResult.isSuccess());

   }

    @Test

    public void testImportCustomerNull() throws IOException {

        MyDataLoaderREST importFile = new MyDataLoaderREST();

        NodeCalculationLoadResult calcResult = importFile.loadFile(null);

        Assert.assertFalse(calcResult.isSuccess());//make sure an exception was not thrown

        Assert.assertEquals(1,calcResult.getlineNumber());//make sure it's not line 0

   }

The service

// The JSR 311 improved service

import au.com.bytecode.opencsv.CSVReader;

@Path("myservice")

public class MyDataLoaderREST {

    @PUT

    @Consumes({"application/json"})

    public CustomerResult loadFile(String csvdata) {

        int lineNumber = 0;// line number outside try to catch can use it

        try {

         

          CSVReader reader = new CSVReader(new StringReader(data));

          if (nextLine.length < 2) {

                    return new CustomerResult(false, 1, "Error reading at line " + lineNumber + " line should have two or more values");

                }

           while ((nextLine = reader.readNext()) != null) {

           }
            return new CustomerResult(false, -1, lineNumber +" lines parsed and imported");

         } catch (ParseException ex) {

            return new CustomerResult(false, lineNumber, "Error parsing data at line " + lineNumber + " " + ex.getMessage());

        } catch (NumberFormatException ex) {

            return new CustomerResult(false, lineNumber, "Error reading number at line " + lineNumber + " " + ex.getMessage());

        } catch (RuntimeException ex) {

            return new CustomerResult(false, lineNumber, "Programmatic error reading number at line " + lineNumber + " " + ex.getMessage());

        }

    }

}


The domain Class @XmlRootElement
@Data // Project Lombok does all my getters and setters for me

// The domain object

@Data // Project Lombok does all my getters and setters for me

public CustomerResult {

     private boolean success;

     private int lineNumber;

     private String errorMessage;

     private Customer customer;

     public CustomerResult(boolean success , int lineNumber,String errorMessage){

       this.success = success;

       this.lineNumber = lineNumber;

       this.errorMessage = errorMessage;

      }

      public CustomerResult(Customer customer){

         this.customer = customer;

      }

      

}


Tuesday, July 3, 2012

What happened to POI

I had to read a XLSX file in POI today

Problem 1:
You can no longer use the old
HSSFWorkbook wb     = new HSSFWorkbook(fileInputStream); 
syntax
or you will get :

The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)
org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)

Problem 2:
So I tried to use the new API
Workbook workbook = WorkbookFactory.create(fileInputStream);

But where is org.apache.poi.ss.usermodel.WorkbookFactory ??

This is because you need another dependably now. Go figure.

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.8</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.8</version>
        </dependency>


Problem 3:

So as a 'good' Java dev, I started with unit testing, but I did not want to read a file in my JUnit test.
I came across this : http://stackoverflow.com/questions/2597271/easy-to-get-a-test-file-into-junit

My JUnit now looks like :

public class TestCalculator {

    @Rule
    public ResourceFile res = new ResourceFile("/Comparative.xlsx");// this file is in my test resources

    @Before
    public void before() throws IOException{
        Assert.assertNotNull( res.getFile());
    }
    @Test
    public void testParseXLSDoc(){
        Calculator c = new Calculator(res.file.getAbsolutePath());//the Java File
        c.parseActiveDocument();  
    }
}

//from stackoverflow
public class ResourceFile extends ExternalResource {

    String res;
    File file = null;
    InputStream stream;

    public ResourceFile(String res) {
        this.res = res;
    }

    public File getFile() throws IOException {
        if (file == null) {
            createFile();
        }
        return file;
    }

    public InputStream getInputStream() {
        return stream;
    }

    public InputStream createInputStream() {
        return getClass().getResourceAsStream(res);
    }

    public String getContent() throws IOException {
        return getContent("utf-8");
    }

    public String getContent(String charSet) throws IOException {
        InputStreamReader reader = new InputStreamReader(createInputStream(),
                Charset.forName(charSet));
        char[] tmp = new char[4096];
        StringBuilder b = new StringBuilder();
        try {
            while (true) {
                int len = reader.read(tmp);
                if (len < 0) {
                    break;
                }
                b.append(tmp, 0, len);
            }
            reader.close();
        } finally {
            reader.close();
        }
        return b.toString();
    }

    @Override
    protected void before() throws Throwable {
        super.before();
        stream = getClass().getResourceAsStream(res);
    }

    @Override
    protected void after() {
        try {
            if (stream != null) {
                stream.close();
            }
        } catch (IOException e) {
            // ignore
        }
        if (file != null) {
            file.delete();
        }
        super.after();
    }

    private void createFile() throws IOException {
        file = new File(".", res);
        InputStream stream = getClass().getResourceAsStream(res);
        try {
            file.createNewFile();
            FileOutputStream ostream = null;
            try {
                ostream = new FileOutputStream(file);
                byte[] buffer = new byte[4096];
                while (true) {
                    int len = stream.read(buffer);
                    if (len < 0) {
                        break;
                    }
                    ostream.write(buffer, 0, len);
                }
            } finally {
                if (ostream != null) {
                    ostream.close();
                }
            }
        } finally {
            stream.close();
        }
    }
}