« Previous month (Feb 2008) | Main | Next page of month (Mrz 2008) »
 20080330 Sunday March 30, 2008

Eclipse vs. Netbeans, Emacs vs. Vi - and why not just beeing pragmatic?

The feedback for my recent post "Eclipse 3.3 or NetBeans 6.0 - with surprising result" was classic - and even a little bit funny.
However it remembered me at the early eclipse days. These days I used Eclipse (1.0 / 2.0) in my commercial projects against the German mainstream - however many developers disliked the idea. In Germany, free, opensource software wasn't really popular. It was in the timeframe about 2001/02. In my opinion the Eclipse SDK was much better, than the majority (perhaps except IntelliJ) of commercial and "enterprise" IDEs and tried to push it. I used a high end IBM ThinkPad Notebook for my daily development work - it was enough to suspect me to work for IBM (Eclipse + ThinkPad = IBM) and so my bias. So the questions I had to answer were like: "You are biased - but - what is the marketshare of Eclipse? How successful is Eclipse in another projects? How many companies already rely on Eclipse?" Such questions were really hard to answer for the 1.0 version :-).

Actually I had no interests to introduce Eclipse, because of strategic or political reasons. From my perspective it was a little bit more efficient than the competition. Several years ago I took a closer look on Netbeans and I like some ideas - like the completeness of the functionality - and start to work with it. I liked it more and more and now I use Netbeans in the majority of my Java EE 5 projects. The reason are simple:

  1. The installation effort is almost non-existent - I download it and start with my work - no additional plugins etc. are needed
  2. The innovation pace is considerable - I just enjoy it - however this is really my "bias", it do not have impact to my commercial work.
  3. The concepts are different - so I searched for "resource perspective" in Netbeans and found the "Favorites" - a totally different approach. It is hard to say what is better - but both concepts are different and work really well.
  4. I enjoy Netbeans Look And Feel. It's just different. I used Eclipse for years - I enjoy the switching between both.

It seems like many developers invested a lot in Eclipse, and do not like the idea to switch to another IDE. In contrary - I like the idea of learning something new - to grasp the key bindings is really not a big deal.

But, who knows? In case the cooperation between Eclipse Foundation and Microsoft, as well as the Microsoft Efforts To Improve Java, or the Java-innovation in Eclipse 4.0 will be significant - I would propably switch back... For now Netbeans 6.0 (I cannot wait for 6.1 / 7.0 final), works a little bit better for my daily work - and I enjoy it. ...although I still use a ThinkPad (but no more from IBM - now it is Lenovo), people suspect me not to be independent and ask me about my relation to Sun. Because I'm a Java Champion the relation should be clear - I'm not a Sun employee :-).

Some advices:

  1. Try from time to time new tools IDEs. It is not really bad idea to switch between Eclipse, IntelliJ, Netbeans and JDeveloper back and forth (not in one project of course, but e.g. in Proof Of Concepts)
  2. Be pragmatic, choose an efficient tool for your job. BUT: your code base shouldn't be dependent on it!
  3. If you are consultant / developer - don't care about esotheric discussions about standards, market share etc. Just take the tool and do the job...
  4. Distrust mainstreams - they are not always the best choice for your particular problem
This entry was inspired by Kristian, and F. Simoni - thanks to both!
Gesendet von admin [Netbeans] ( March 30, 2008 11:07 AM ) Permalink | Kommentare [4]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080329 Saturday March 29, 2008

Upcoming Java EE 5 / 6 Training, Courses and Events in Germany

I just synced and refreshed my training calendar. The next trainings I'm involved are:

I will talk at the JAX conference about Java FX and maintainable RIAs as well. Feel free to contact me for a chat, in case you see hanging me around :-).
Gesendet von admin [Events] ( March 29, 2008 03:54 PM ) Permalink | Kommentare [0]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080327 Thursday March 27, 2008

How To Find / Print Out Netbeans 6.1 Key Bindings

Eclipse developers, who try out Netbeans 6.0/1 are often confused by the different key bindings. Netbeans comes already with a nice overview of the shortcuts. You can access the document via:  Menu (Help -> Keyboard Shortcuts Card). It is a pointer to a PDF-document - so you will have to have e.g. the Acrobat in the path. The document is stored in the folder: \[Netbeans_Installation]\nb6.1\shortcuts.pdf.

All the shortcuts can be easily printed out. What's interesting as well - you could change the default bindings to Eclipse / Emacs / Netbeans 5.5.

This feature is available from: Menu (Tools -> Options -> Keymap). Just change your prefered bindings from the "Profile" drop-down. The Eclipse-bindings were incomplete and partially wrong in the past. However this was corrected - and is available as a patch for Netbeans 6.0/6.01.

Gesendet von admin [Netbeans] ( March 27, 2008 09:00 AM ) Permalink | Kommentare [1]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080326 Wednesday March 26, 2008

Why some of the Java EE / J2EE projects are inefficient ...or at least suboptimal

  1. Architects are more skilled in PowerPoint, than popular Java IDEs (OpenOffice ist still rare in real world :-))
  2. It takes several DVDs, sometimes hours, even to install the basic infrastructure (like appserver and database)
  3. Some popular servers take several minutes to start and deploy - you have to repeat this procedure several times a day
  4. It takes longer to open a case (and reproduce a problem) for a bug of the appserver, than fix it by yourself (of course if you had the source :-))
  5. It is hard to find developer hardware, where the "enterprise" development tools run efficienlty - ...and because they were expensive, it is hard to get rid of them...
  6. The architects love layers and tiers - several mapping procedures are needed just to pass a persistent entity from the persistent layer to the presentation
  7. Everything is configurable, replaceable and mockable. The XML overhead is huge. The question is: When did you really needed to replace something in your passed projects?
  8. Either it is waterfallish, or agile with all buzzwords and strange rituals. Both sides could be extremely inefficent. It seems like sometimes it is hard to be just rationale...
  9. Developers are sometimes too extreme: either everything is overengineered with millions of patterns or best practices, or hacked down in "go to spaghetti" fashion
  10. "The thrill is gone..." many developers, architects and managers just lost they enthusiasm and passion. This is one of the main reasons, why many projects are just so inefficient...
  11. HA, Clustering, etc. is used even for "guestbook-like" applications. Complexity rules!
  12. Strange QA rules (like documenting obvious getters/setters) drive the development and maintenance costs

Just my observation hacked down in 2 minutes in Starbuck/Munich :-) What's your favorite? Do I missed something?
Gesendet von admin [Java EE 5 Architectures And Idioms] ( March 26, 2008 10:50 AM ) Permalink | Kommentare [13]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080325 Tuesday March 25, 2008

Why sometimes a reference to an EntityManager inside JPA Entities is needed

In all of my more complex projects, we had to reference  the EntityManager from inside the JPA-Entities. Especially in rich - so entities with business logic, we had to reference the EntityManager somehow. The requirements for referencings the EntityManager were various - and because of NDAs (I'm working mainly as self-employed consultant) hard to share :-) . However I ran into similar issues in a demo application from http://p4j5.dev.java.net as well. The RunAndBike application manages the training unit. BikingUnits  - as well as RunningUnits (both inherit from Unit).

The JPA-Entity Season represents a Training Season (e.g. all units of the year 2008) and is associated with the User. It holds a reference to N abstract Units:

@Entity
@Table(name="TB_SEASON")
public class Season implements Serializable {
   
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long seasonId;

    @Column(name="season_year")
    private int year;

    @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
    @OrderBy("trainingBegin DESC")
    private List<Unit> trainingUnits;

//lot of code omitted 

}

So you can store BikingUnit:

public class BikingUnit extends Unit {

    private int avgCadence;
    @Enumerated(EnumType.STRING)
    @Column(name="t_type")
    private TrainingType type;
   
    @ManyToOne(cascade=CascadeType.ALL)
    private Bike bike;

//some code omitted
 }

or RunningUnit in it:

@Entity
@NamedQuery(name=RunningUnit.ALL_UNITS,query="Select r From RunningUnit r")
public class RunningUnit extends Unit {
   
    @ManyToOne(cascade=CascadeType.ALL)
    private Shoes shoes;

//some code omitted
 }

 with their specific states and behaviors.

So far, everything works really well. You can manage your units conveniently. Beyond that this solution is extendible, because you could easily introduce just another entity which inherits from the abstract Unit (I got already some request from triathletes for swimming....).

However I just wanted to provide really basic report functionality and compute the average speed. In that particular case it the average speed of Biking and Running Units should be computed separetely (it is not very impressive to have 25km avg speed on a bike, but is noteworthy to be able to run such fast).

To achieve this, I have to filter out all Running/Biking units first in the Season entity: 

    public List<RunningUnit> getRunningUnits() {
        List<RunningUnit> retVal = new ArrayList<RunningUnit>();
        for (Unit unit : getTrainingUnits()) {
            if (unit instanceof RunningUnit)
                retVal.add((RunningUnit) unit);
        }
        Collections.sort(retVal, new UnitComparator());
        return retVal;
    }
It works fine, because you will hardly have more, than 356 units a year - so it is not a huge problem. In case we would have several thousands units - we would run into performance / memory issue with this approach. In that case it would be more appropriate to query the database and not traverse the objects. ...but to access the database, we would need an EntityManager - but it cannot be injected yet. In all of my projects we stored it somehow, mostly in a ThreadLocal.

In real world projects, we needed the access to the EntityManager because of some historization, searches and filtering (mostly because of performance optimization). I always had a bad feeling - because obtaining a reference from an JPA-Entities through a ThreadLocal seemed always a "hack" to me.

Other bloggers ran in the issue as well. In Frank Cornelis wish list, he suggest a query extension:

 

@Entity
@NamedQueries(@NamedQuery(name="byName", query="FROM MyEntity WHERE name = :name"))
public class MyEntity {
@Id
String name;

// ...

public static Query createQueryWhereName(EntityManager em, String name) {
Query query = em.createNamedQuery("byName");
query.setParameter("name", name);
return query;
}
}

The question remains -> how the EntityManager is going to be obtained in his case?

I try to push this issue in my work as EG in JPA 2.0 (JSR-317) -> so if you had similar issues just leave a comment here or write a feedback to the group. You can download the whole application from http://p4j5.dev.java.net (from SVN). The project name is: RunAndBikeApplication (the EAR), and RunAndBikeDB (both Netbeans 6.0 projects, tested on Glassfish v2). The application is available online as well (but slow because of bandwidth).

One solution could be the introduction of a specific LifecycleListener and Lifecycle Call Back methods like:


@PostAttach
public void postAttach(EntityManager manager){

}

@PreDetach
public void preDetach(){}

I will cover the issue of Rich Domain Models and JPA in one of the upcoming issues of the german magazine JavaSPEKTRUM in more detail as well. You can dowload the part 2 for free.

Any thoughts?

Gesendet von admin [Java EE 5 Architectures And Idioms] ( March 25, 2008 11:16 AM ) Permalink | Kommentare [6]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080324 Monday March 24, 2008

Delving into JSF Scaffolding with JPA on Netbeans 6.1

In Netbeans 6.1 the JSF-Crud scaffolding wizard is available again (it disappeared in 6.0). It allows the creation of CRUD-JSF applications from any tables you like. Even the relations will be considered in the generation. The scaffolding is a three-step process:

  1. You will need Netbeans 6.1 beta. After the installation create a WebApplication (Strg+Shift+N -> folder Web -> Webapplication)
  2. Right click on the project and choose: "Entity Classes From Database". This wizard creates JPA-Entities from a JDBC-source. You have just to choose an existing JDBC-source. The persistence.xml will be created for you. Noteworthy: on the second wizard page, the checkbox: "Generate Named Query Annotations For Persistent Fields" can be unchecked. Then all the NamedQueries wouldn't be generated. This shortens the startup and deployment time.
  3. Right click on the project again and choose: "JSF Pages From Entity Class". This will generate JSF pages for existing JPA entities. Then just "Run" the project, and you should be able to browse in the database.

The generation of the entities is clean. Netbeans 6.1 uses field-based injection (what I prefer):

@Entity
@Table(name = "CUSTOMER")
@NamedQueries({

@NamedQuery(name = "Customer.findByCustomerId", query = "SELECT c FROM Customer c WHERE c.customerId = :customerId"),
@NamedQuery(name = "Customer.findByZip", query = "SELECT c FROM Customer c WHERE c.zip = :zip"),
@NamedQuery(name = "Customer.findByName", query = "SELECT c FROM Customer c WHERE c.name = :name"),
@NamedQuery(name = "Customer.findByAddressline1", query = "SELECT c FROM Customer c WHERE c.addressline1 = :addressline1"),
@NamedQuery(name = "Customer.findByFax", query = "SELECT c FROM Customer c WHERE c.fax = :fax"),

(... and remaining queries)})
public class Customer implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Column(name = "CUSTOMER_ID", nullable = false)
    private Integer customerId;
    @Column(name = "ZIP", nullable = false)
    private String zip;
    @Column(name = "NAME")
    private String name;
    @Column(name = "ADDRESSLINE1")
    private String addressline1;

    @JoinColumn(name = "DISCOUNT_CODE", referencedColumnName = "DISCOUNT_CODE")
    @ManyToOne
    private DiscountCode discountCode;
 

//additional attributes

    public Customer() {    }

    public Customer(Integer customerId) {
        this.customerId = customerId;
    }

    public Customer(Integer customerId, String zip) {
        this.customerId = customerId;
        this.zip = zip;
    }

    public Integer getCustomerId() {
        return customerId;
    }

//remaining Getters / Setters
}

 
The persistent Entities are absolutely usable in real-world projects, however some things like inheritance, unidirectional 1:n relations, sometimes just cannot be derived from the schema.

Netbeans generates the glue between the JPA-entities and the JSF-view as well. It generates a controller (the CustomerController):

public class CustomerController {
    private Customer customer = null;
    private List<Customer> customers = null;
    @Resource
    private UserTransaction utx = null;
    @PersistenceUnit(unitName = "JSFScaffoldingPU")
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }
    public int batchSize = 5;
    private int firstItem = 0;
    private int itemCount = -1;

    public SelectItem[] getCustomersAvailableSelectMany() {
        return getCustomersAvailable(false);
    }

    public SelectItem[] getCustomersAvailableSelectOne() {
        return getCustomersAvailable(true);
    }

    private SelectItem[] getCustomersAvailable(boolean one) {
        List<Customer> allCustomers = getCustomers(true);
        int size = one ? allCustomers.size() + 1 : allCustomers.size();
        SelectItem[] items = new SelectItem[size];
        int i = 0;
        if (one) {
            items[0] = new SelectItem("", "---");
            i++;
        }
        for (Customer x : allCustomers) {
            items[i++] = new SelectItem(x, x.toString());
        }
        return items;
    }

    public Customer getCustomer() {
        if (customer == null) {
            customer = getCustomerFromRequest();
        }
        if (customer == null) {
            customer = new Customer();
        }
        return customer;
    }

    public String listSetup() {
        reset(true);
        return "customer_list";
    }

    public String createSetup() {
        reset(false);
        customer = new Customer();
        return "customer_create";
    }

    public String create() {
        EntityManager em = getEntityManager();
        try {
            utx.begin();
            em.persist(customer);
            DiscountCode discountCode = customer.getDiscountCode();
            if (discountCode != null) {
                discountCode.getCustomerCollection().add(customer);
                discountCode = em.merge(discountCode);
            }
            utx.commit();
            addSuccessMessage("Customer was successfully created.");
        } catch (Exception ex) {
            try {
                ensureAddErrorMessage(ex, "A persistence error occurred.");
                utx.rollback();
            } catch (Exception e) {
                ensureAddErrorMessage(e, "An error occurred attempting to roll back the transaction.");
            }
            return null;
        } finally {
            em.close();
        }
        return listSetup();
    }

    public String detailSetup() {
        return scalarSetup("customer_detail");
    }

    public String editSetup() {
        return scalarSetup("customer_edit");
    }

    private String scalarSetup(String destination) {
        reset(false);
        customer = getCustomerFromRequest();
        if (customer == null) {
            String requestCustomerString = getRequestParameter("jsfcrud.currentCustomer");
            addErrorMessage("The customer with id " + requestCustomerString + " no longer exists.");
            String relatedControllerOutcome = relatedControllerOutcome();
            if (relatedControllerOutcome != null) {
                return relatedControllerOutcome;
            }
            return listSetup();
        }
        return destination;
    }

    public String edit() {
        CustomerConverter converter = new CustomerConverter();
        String customerString = converter.getAsString(FacesContext.getCurrentInstance(), null, customer);
        String currentCustomerString = getRequestParameter("jsfcrud.currentCustomer");
        if (customerString == null || customerString.length() == 0 || !customerString.equals(currentCustomerString)) {
            String outcome = editSetup();
            if ("customer_edit".equals(outcome)) {
                addErrorMessage("Could not edit customer. Try again.");
            }
            return outcome;
        }
        EntityManager em = getEntityManager();
        try {
            utx.begin();
            DiscountCode discountCodeOld = em.find(com.abien.scaffolding.Customer.class, customer.getCustomerId()).getDiscountCode();
            customer = em.merge(customer);
            DiscountCode discountCodeNew = customer.getDiscountCode();
            if (discountCodeOld != null && !discountCodeOld.equals(discountCodeNew)) {
                discountCodeOld.getCustomerCollection().remove(customer);
                discountCodeOld = em.merge(discountCodeOld);
            }
            if (discountCodeNew != null && !discountCodeNew.equals(discountCodeOld)) {
                discountCodeNew.getCustomerCollection().add(customer);
                discountCodeNew = em.merge(discountCodeNew);
            }
            utx.commit();
            addSuccessMessage("Customer was successfully updated.");
        } catch (Exception ex) {
            try {
                String msg = ex.getLocalizedMessage();
                if (msg != null && msg.length() > 0) {
                    addErrorMessage(msg);
                } else if (getCustomerFromRequest() == null) {
                    addErrorMessage("The customer with id " + currentCustomerString + " no longer exists.");
                    utx.rollback();
                    return listSetup();
                } else {
                    addErrorMessage("A persistence error occurred.");
                }
                utx.rollback();
            } catch (Exception e) {
                ensureAddErrorMessage(e, "An error occurred attempting to roll back the transaction.");
            }
            return null;
        } finally {
            em.close();
        }
        return detailSetup();
    }

    public String destroy() {
        customer = getCustomerFromRequest();
        if (customer == null) {
            String currentCustomerString = getRequestParameter("jsfcrud.currentCustomer");
            addErrorMessage("The customer with id " + currentCustomerString + " no longer exists.");
            String relatedControllerOutcome = relatedControllerOutcome();
            if (relatedControllerOutcome != null) {
                return relatedControllerOutcome;
            }
            return listSetup();
        }
        EntityManager em = getEntityManager();
        try {
            utx.begin();
            customer = em.merge(customer);
            DiscountCode discountCode = customer.getDiscountCode();
            if (discountCode != null) {
                discountCode.getCustomerCollection().remove(customer);
                discountCode = em.merge(discountCode);
            }
            em.remove(customer);
            utx.commit();
            addSuccessMessage("Customer was successfully deleted.");
        } catch (Exception ex) {
            try {
                ensureAddErrorMessage(ex, "A persistence error occurred.");
                utx.rollback();
            } catch (Exception e) {
                ensureAddErrorMessage(e, "An error occurred attempting to roll back the transaction.");
            }
            return null;
        } finally {
            em.close();
        }
        String relatedControllerOutcome = relatedControllerOutcome();
        if (relatedControllerOutcome != null) {
            return relatedControllerOutcome;
        }
        return listSetup();
    }

    private Customer getCustomerFromRequest() {
        String theId = getRequestParameter("jsfcrud.currentCustomer");
        return (Customer) new CustomerConverter().getAsObject(FacesContext.getCurrentInstance(), null, theId);
    }

    private String getRequestParameter(String key) {
        return FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(key);
    }

    public List<Customer> getCustomers() {
        if (customers == null) {
            customers = getCustomers(false);
        }
        return customers;
    }

    public List<Customer> getCustomers(boolean all) {
        EntityManager em = getEntityManager();
        try {
            Query q = em.createQuery("select object(o) from Customer as o");
            if (!all) {
                q.setMaxResults(batchSize);
                q.setFirstResult(getFirstItem());
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    private void ensureAddErrorMessage(Exception ex, String defaultMsg) {
        String msg = ex.getLocalizedMessage();
        if (msg != null && msg.length() > 0) {
            addErrorMessage(msg);
        } else {
            addErrorMessage(defaultMsg);
        }
    }

    public static void addErrorMessage(String msg) {
        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg);
        FacesContext.getCurrentInstance().addMessage(null, facesMsg);
    }

    public static void addSuccessMessage(String msg) {
        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg);
        FacesContext.getCurrentInstance().addMessage("successInfo", facesMsg);
    }

    public Customer findCustomer(Integer id) {
        EntityManager em = getEntityManager();
        try {
            Customer o = (Customer) em.find(Customer.class, id);
            return o;
        } finally {
            em.close();
        }
    }

    public int getItemCount() {
        if (itemCount == -1) {
            EntityManager em = getEntityManager();
            try {
                itemCount = ((Long) em.createQuery("select count(o) from Customer as o").getSingleResult()).intValue();
            } finally {
                em.close();
            }
        }
        return itemCount;
    }

    public int getFirstItem() {
        getItemCount();
        if (firstItem >= itemCount) {
            if (itemCount == 0) {
                firstItem = 0;
            } else {
                int zeroBasedItemCount = itemCount - 1;
                double pageDouble = zeroBasedItemCount / batchSize;
                int page = (int) Math.floor(pageDouble);
                firstItem = page * batchSize;
            }
        }
        return firstItem;
    }

    public int getLastItem() {
        getFirstItem();
        return firstItem + batchSize > itemCount ? itemCount : firstItem + batchSize;
    }

    public int getBatchSize() {
        return batchSize;
    }

    public String next() {
        reset(false);
        getFirstItem();
        if (firstItem + batchSize < itemCount) {
            firstItem += batchSize;
        }
        return "customer_list";
    }

    public String prev() {
        reset(false);
        getFirstItem();
        firstItem -= batchSize;
        if (firstItem < 0) {
            firstItem = 0;
        }
        return "customer_list";
    }

    private String relatedControllerOutcome() {
        String relatedControllerString = getRequestParameter("jsfcrud.relatedController");
        String relatedControllerTypeString = getRequestParameter("jsfcrud.relatedControllerType");
        if (relatedControllerString != null && relatedControllerTypeString != null) {
            FacesContext context = FacesContext.getCurrentInstance();
            Object relatedController = context.getApplication().getELResolver().getValue(context.getELContext(), null, relatedControllerString);
            try {
                Class<?> relatedControllerType = Class.forName(relatedControllerTypeString);
                Method detailSetupMethod = relatedControllerType.getMethod("detailSetup");
                return (String) detailSetupMethod.invoke(relatedController);
            } catch (ClassNotFoundException e) {
                throw new FacesException(e);
            } catch (NoSuchMethodException e) {
                throw new FacesException(e);
            } catch (IllegalAccessException e) {
                throw new FacesException(e);
            } catch (InvocationTargetException e) {
                throw new FacesException(e);
            }
        }
        return null;
    }

    private void reset(boolean resetFirstItem) {
        customer = null;
        customers = null;
        itemCount = -1;
        if (resetFirstItem) {
            firstItem = 0;
        }
    }
    private Map<Customer, String> asString = null;

    public Map<Customer, String> getAsString() {
        if (asString == null) {
            asString = new HashMap<Customer, String>() {

                @Override
                public String get(Object key) {
                    return new CustomerConverter().getAsString(FacesContext.getCurrentInstance(), null, (Customer) key);
                }
            };
        }
        return asString;
    }
    private Validator entityCreationValidator = null;

    public Validator getEntityCreationValidator() {
        if (entityCreationValidator == null) {
            entityCreationValidator = new Validator() {

                public void validate(FacesContext facesContext, UIComponent component, Object value) {
                    CustomerConverter converter = new CustomerConverter();
                    String newCustomerString = converter.getAsString(FacesContext.getCurrentInstance(), null, new Customer());
                    String customerString = converter.getAsString(FacesContext.getCurrentInstance(), null, customer);
                    if (!newCustomerString.equals(customerString)) {
                        createSetup();
                        throw new ValidatorException(new FacesMessage("Could not create customer. Try again."));
                    }
                }
            };
        }
        return entityCreationValidator;
    }

}

 Registers it as a managed-bean in the faces-config.xml:

<faces-config version="1.2"
    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-facesconfig_1_2.xsd">
    <managed-bean>
        <managed-bean-name>customer</managed-bean-name>
        <managed-bean-class>com.abien.scaffolding.CustomerController</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>

</faces-config>

 The CustomerController is a POJO. So the transaction management, resource management are implemented inside. The CustomerController is the mediator between the view and the persistence. However it implements the presentation and some business logic as well. For every entity a converter class is created:

public class CustomerConverter implements Converter {

    public Object getAsObject(FacesContext facesContext, UIComponent component, String string) {
        if (string == null || string.length() == 0) {
            return null;
        }
        Integer id = new Integer(string);
        com.abien.scaffolding.CustomerController controller = (com.abien.scaffolding.CustomerController) facesContext.getApplication().getELResolver().getValue(facesContext.getELContext(), null, "customer");

        return controller.findCustomer(id);
    }

    public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
        if (object == null) {
            return null;
        }
        if (object instanceof Customer) {
            Customer o = (Customer) object;
            return o.getCustomerId() == null ? "" : o.getCustomerId().toString();
        } else {
            throw new IllegalArgumentException("object:" + object + " of type:" + object.getClass().getName() + "; expected type: com.abien.scaffolding.Customer");
        }
    }

The CustomerConverter is a javax.faces.convert.Converter, his responsibility is the conversion of domain objects and flat string. The javadoc says: "Converter is an interface describing a Java class that can perform Object-to-String and String-to-Object conversions between model data objects and a String representation of those objects that is suitable for rendering"

Netbeans 6.1 cares about the registration of the converter in the faces-config.xml:

    <converter>
        <converter-for-class>com.abien.scaffolding.Customer</converter-for-class>
        <converter-class>com.abien.scaffolding.CustomerConverter</converter-class>
    </converter>

Netbeans 6.1 scaffolding is an interesting way to start with JSF programming to learn basic JSF and JPA stuff. Some thoughts:

 

  1. Also the JSF editor is really useful (syntax highligting, code HTML and Java code completion), I wasn't able to edit them visually afterwards. This can be a serious limitation.
  2. The controller is a POJO. So it has to care about the transactions and resource management. The usage of EJB 3 would result in much less code. However it isn't possible now to register an EJB 3 in the faces-config.xml. WebBeans and Seam will be able to provide this, but now it isn't just possible with standard means... However the Controller could act as a mediator and delegate the invocations to an Stateless Session Bean (I'm thinking seriously about hacking/extending the wizard)
  3. The controller mixes the presentation and business logic - so it isn't a best practice - but good enough for hacking a smaller project.
  4. I had some trouble with imports. In the Controllers/Converters the package statement was incorrect. (scaffolding was swallowed. com.abien -> com.abien.scaffolding). I had to change it manually. This is a new problem - in 5.5.1 it worked just well.
Btw. This functionality comes with Netbeans 6.1 EE (the left choice) - no additional plugins are required :-).
Gesendet von admin [Netbeans] ( March 24, 2008 12:39 PM ) Permalink | Kommentare [3]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080322 Saturday March 22, 2008

Eclipse 3.3 or NetBeans 6.0 - with surprising result

This interesting JavaWorld article compares Eclipse 3.3.1 and Netbeans 6.01 with a surprising result. The following summary, really amazed me:

Table 2. NetBeans 6.0 vs. Eclipse 3.3: Rated

Feature Weighting Eclipse 3.3 NetBeans 6.0
Ease of use/editing features 40% 2.8 3.6
Scripting/other languages 10% 3.0 3.6
Enterprise support 20% 3.2 3.0
Plugin ecosystem 30% 3.8 2.7
Total score   3.20 3.21

[the table was copied from the fourth page]

It really surprised me, that Netbeans's editor was higher rated than Eclipse's. I prefer the Netbeans editing experience, but my opinion isn't really objective :-). I just like Netbeans's editor more, without knowing the reason. The small built-in wizards like alt+insert make the coding experience more fluent - but this is really my subjective view. Most of the developers prefer Eclipse, because of the editing experience (they often miss in Netbeans e.g. the "Link With Editor" button). On the other hand, Eclipse was higher rated in the Enterprise support, what I really cannot see. Netbeans comes already with really good built-in Java EE 5 support. Applications servers like BEA WLS, JBoss, Glassfish in 6.1 WebSphere are supported as well out-of-the-box. Netbeans comes with SQL-Explorer, Visual JSF designer, HTML-Editor, XML-Support, profiling support for application servers. All this functionality is only partially available on the Eclipse Side - sometimes only with additional commercial plugins like MyEclipse. The Enterprise Support is one of the reasons, why I prefer Netbeans over Eclipse in my Java EE 5 projects.

Eclipse also do not have a visual UML support. The excerpt from the article below simply isn't true:
 
"...For example, when it comes to UML modeling, NetBeans has a built-in modeling tool that supports UML (including use case, class, collaboration, sequence, and activity diagrams). Code can be abstracted into a UML diagram, and UMLs converted to code. Eclipse, by comparison, offers the Enterprise Modeling Framework (EMF), which is a platform for building tools, and the graphical editor framework (GEF). If you install both of these packages, then configure them, you'll be ready to start modeling your enterprise architecture in UML. You'll have more features than you would in NetBeans, but you'll work harder to get them installed, configured, and running..."

The EMF is only a subset of MOF (Meta Object Facility), GEF is used to build Editors from metamodels. The author of the article meant perhaps GMF (Graphical Modelling Framework) - however no of this frameworks is able to generate UML-Diagrams from source code. This is Netbeans unique feature. However the availability of EMF and GMF and it's metamodelling capabilities is a huge advantage of Eclipse, especially if you would like to build code generators, ...but it wasn't mentioned in this article. Eclipse comes only with the UML metamodel - but I do not know any visual designer (did I miss something?).

I also do not see, that the amount of available plugins, indicates the quality of the IDE. You just need less plugins for Netbeans to complete your work. Actually I only use the UML-plugin - and this one only for the documentation of high level concepts.

I explained my view of Eclipse and Netbeans in the post "Thinking loud about Eclipse and Netbeans"

Gesendet von admin [Netbeans] ( March 22, 2008 06:29 PM ) Permalink | Kommentare [18]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080321 Friday March 21, 2008

Netbeans 6.X Docs - also offline available/downloadable

One of the huge advantages of Netbeans is it's amount of free documentation, resources, demos and podcast / screencast coverage. Most of the resources are available online, which is not always easily accessible. Especially in the "dead-time" (trains, plains and hotels) internet access is not always available. Part of the netbeans documentation is available for download (42 MB) as well. The downloadible package contains resources from Basic IDE, over Java programming, Java EE, Ruby and Mobile to C/C++ programming. Gesendet von admin [Netbeans] ( March 21, 2008 12:43 PM ) Permalink | Kommentare [0]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080320 Thursday March 20, 2008

Free Article about Visual JSF Creation, EJB 3 and Maintenance ...with Netbeans and Glassfish as a sample

JSF was from the beginning intended for creation with WYSIWYG tools and data binding. However there is no perfect tool, even worse, sometimes the visual designer becomes inconsistent and your work could get lost. I wrote an article (in German) about building maintainable applications rapidly (with visual composition tools) for JavaSPEKTRUM with presentation tier patterns like Model View Presenter (actually Passive View, Supervising Controller) and used as a sample Netbeans 6.0 (data binding, visual designer, and UML) and Glassfish v2. You can dowload it for free. You will find the whole source code of the application in the http://p4j5.dev.java.net (the project name is RunAndBikeApplication) repository as well. Feedback is, as always, appreciated. In the next issue of JavaSpektrum I will focus on the EJB 3 backend. The issue after on Domain Driven Design.
Gesendet von admin [Netbeans] ( March 20, 2008 04:42 PM ) Permalink | Kommentare [0]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]

 20080318 Tuesday March 18, 2008

Starting Components On Java EE 5 Appserver In a Portable Way

The last time I was frequently asked how to start components / initialize the infrastructure during server's startup. I saw several solutions already, some really esotheric, most of them not portable across servers. However the solution is really simple. All you need is a servlet with an init method:

public class BootstrapServlet extends HttpServlet {   

     //optional, only needed in case you would like to startup a session bean as well
    @EJB
    private BootstrapBean bootstrap;
   
    @Override
    public void init(){
        System.out.println("##################################### Hello World init");
    }

   public void doGet...

}

 To cause the application to init the servlet at startup, you have to include the <load-on-startup> tag in the web.xml.

 <web-app version="2.5" 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-app_2_5.xsd">
    <servlet>
        <servlet-name>BootstrapServlet</servlet-name>
        <servlet-class>com.abien.bootstrap.BootstrapServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>BootstrapServlet</servlet-name>
        <url-pattern>/BootstrapServlet</url-pattern>
    </servlet-mapping>
 </web-app>

 

The spec says: "...The load-on-startup element indicates that this servlet should be loaded (instantiated and have its init() called) on the startup of the web application. The optional contents of these
        element must be an integer indicating the order in which the servlet should be loaded. If the value is a negative integer, or the element is not present, the container is free to load the servlet
        whenever it chooses. If the value is a positive integer or 0, the container must load and initialize the servlet as the application is deployed. The container must guarantee that
        servlets marked with lower integers are loaded before servlets marked with higher integers. The container may choose the order of loading of servlets with the same load-on-start-up value...
"

If you would like to initialize a Session Bean during the startup just inject the Session Bean into the servlet -> and it has to be initialized to. The EJB 3.1 spec will come with a Singleton Bean which will solve this problem - even without a servlet... I checked in this sample code as a Netbeans 6.1/Glassfish v2 into http://p4j5.dev.java.net, the project name is ServletBootStrapping.

Gesendet von admin [Java EE 5 Architectures] ( March 18, 2008 09:46 PM ) Permalink | Kommentare [1]
[my website] [This entry is based on / extends my books: Enterprise Architekturen, Leitfaden fuer effiziente Software-Entwicklung and: Java EE 5 Architekturen, Patterns und Idiome]




License
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.0 License.