David R. Heffelfinger
- All
- General
- Java
- BlackBerry
- NetBeans
- Software Development
- GlassFish
- Linux
NetBeans IceFaces Plugin First Impressions
Recently Sun announced that there will be no further development for Project Woodstock. Some maintenance will be done in the project, but no new features are planned.
This announcement affects NetBeans Visual Web projects, since they use Project Woodstock JSF components behind the scenes.
Sun is now recommending that current Woodstock applications be migrated to IceFaces. An IceFaces plugin has been available for a while to develop JSF applications visually using the IceFaces components, the plugin works in a way that is very similar to the standard Visual Web JSF NetBeans plugin.
Since Woodstock will no longer be developed, I figured now is a good time to take the NetBeans IceFaces plugin for a spin, to see how well it works, turns out, it doesn't work very well, at least the current version available from the NetBeans Plugin Manager.
The first issue I ran into is that the Java package for managed beans defaults to the project name and cannot be changed. The project name never adheres to standard Java package naming conventions (i.e. com.mycompany.packagename), therefore we end up with classes in nonstandard packages.
Under Windows, I wasn't able to drag components from the palette into the navigator page, this it is possible using NetBeans standard Visual Web JSF functionality (oddly enough, I was able to do this under Ubuntu Linux). I prefer dropping the components in the navigator page instead of directly in the JSP design view, since dropping the components in the design view results in CSS absolute positioning. I'd rather use the JSF standard way of placing components, which is using a panel grid for layout right inside a JSF form component. This issue can be worked around, by placing a grid in the design view then manually removing its "style" attribute, but it feels cleaner to just drop components in the navigator page.
Also, applications developed using IceFaces cannot be run under GlassFish 3. Maybe there is some faces-config.xml or web.xml incantation that needs to be done in order to make this work, I didn't try and didn't research it, all I did was develop a simple test application and try to deploy it.
Here is a partial stack trace from the GlassFish 3 log when attempting to deploy an IceFaces application developed using the NetBeans Plugin:
SEVERE: Can't find TLD for location [http://java.sun.com/jsf/core]. JAR containing the TLD may not be in the classpath
SEVERE: Can't find TLD for location [http://java.sun.com/jsf/html]. JAR containing the TLD may not be in the classpath
SEVERE: Failed to execute JSP lifecycle.
java.lang.IllegalStateException: ICEfaces parser unable to determine JSF implementation ViewTag class.
at com.icesoft.faces.webapp.parser.Parser.parse(Parser.java:152)
at com.icesoft.faces.application.D2DViewHandler.renderResponse(D2DViewHandler.java:528)
at com.icesoft.faces.application.D2DViewHandler.renderView(D2DViewHandler.java:159)
Additionally, when working with standard Visual Web JSF, the "for" attribute of labels and message components can be set by ctrl+shift dragging from the label or message component to the target component. This functionality is not available in the IceFaces visual plugin.
One thing I was looking forward to was finding out if IceFaces wouldn't suffer from my Woodstock pet peeve. In JSF 1.2, the "label" attribute was added to all standard JSF input components. This attribute results in user friendly error messages. The JSF message and messages components by default use the component id to identify a validation error in a component, which is meaningless to the user. The label attribute allows the error message to use a user friendly label, as opposed to the component ID.
Woodstock components have a label attribute that, in addition to enabling user friendly error messages as described in the previous paragraph, actually adds a label to the component. Kind of neat, but it also makes it very difficult to right align all the components and left align all the labels like it is usually done in web applications. Therefore, when using Woodstock, there was always this catch-22 between user friendly error messages and ease of component alignment.
One of the first things I tried when testing the IceFaces NetBeans plugin was to set the "label" attribute on an input text component, and set the component's "required" attribute to "true". I ran the application, submitted the page (purposefully leaving the required input field blank) braced myself and bam! The component id is still shown in the error message, as opposed to my user friendly label, bummer!
I googled a bit and read some people had success with the label attribute using Facelets. I installed the NetBeans Facelets plugin and redid my simple application using Facelets. This time the label attribute "kicked in" and worked as expected.
Although I am not afraid to code things by hand, especially with excellent code/tag completion support like the one found in NetBeans, Visual web provides a nice way to preview a page while working with it, this functionality is lost when using Facelets with NetBeans.
I guess the plugin is "almost there", but the fact that the "label" attribute is ignored is a big bummer. Also, I miss the ability to drop components from the palette into the navigator window, bypassing CSS absolute positioning.
Other functionality we are used to from Visual Web JSF is still there. A request scoped managed bean is created for each IceFaces visual page we create (one caveat, the default package for these managed beans can't be changed, it defaults to the project name, which does not adhere to standard java package naming conventions). Also, double clicking on a command button automatically creates a method on the managed bean to handle its action event.
Although I don't have a lot of experience with this plugin, my impression is that it is better than coding JSF applications by hand, but I really miss some of the missing features from the standard Visual Web JSF functionality.
I would also like to clarify that this entry is not meant to knock IceFaces. I've seen the IceFaces demo apps and I can see that it eases creation of very sophisticated and elegant web applications, I just found some issues with the IceFaces NetBeans plugin that I hope will be addressed in the near future.
Posted at 06:56PM Dec 18, 2008 by David R. Heffelfinger in NetBeans | Comments[4]
NetBeans Podcast Interview Just Finished
My NetBeans Podcast Interview just finished a few minutes ago.
We talked about NetBeans, Java EE and my book.
There were some interesting questions regarding NetBeans and Java EE, in addition to questions about how I got involved with my publisher, Packt Publishing, and how I got the idea of writing a book about NetBeans.
The podcast is scheduled to come out sometime this month.
Posted at 07:10AM Dec 12, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
NetBeans Podcast Interview
I was just invited to be interviewed in the next NetBeans Podcast. Geertjan Wielenga and Lloyd Dunn will be asking me questions about my new book, Java EE 5 Development with NetBeans 6
I'm very excited about the opportunity to be a part of an episode of the NetBeans podcast, I've been listening to it for the past several month to keep up to date on what is going on in the NetBeans world.
Posted at 08:59PM Dec 05, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
NetBeans 6.5 Final Now Available
It has come to my attention that NetBeans 6.5 final is now available.
It can be downloaded from the NetBeans web site.
If you would like to learn the ins and outs of enterprise Java development, my book, Java EE 5 Development with NetBeans 6, may help.
Posted at 10:06AM Nov 19, 2008 by David R. Heffelfinger in NetBeans | Comments[4]
JavaRanch Book Promotion Over
The JavaRanch book promotion we had this week for my new book, Java EE 5 Development With NetBeans 6 is now over.
There were many good questions over the week about the book and NetBeans in general.
I would like to personally congratulate all the "Java Ranchers" that won a free copy of the book:
-
renu richard
Peter Johnson
Rogerio Kioshi
Ankit Garg
Posted at 07:13PM Nov 08, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
Java EE Development With NetBeans 6 has been published
I'm pleased to announce that my latest book, Java EE Development With NetBeans 6 has been published.
The book covers several aspects of Java EE development using NetBeans, such as:
-
Visual Web JSF Development
JPA development, including automatic JPA generation from existing database schemas.
Automated JSF CRUD application generation from existing JPA entities
EJB 3 development, including session and message driven beans.
Debugging Java EE applications with the NetBeans debugger
Profiling Java EE applications with the NetBeans profiler
Additional Java EE Development Topics
Posted at 07:43AM Oct 31, 2008 by David R. Heffelfinger in NetBeans | Comments[1]
NetBeans Book Promotion Next Week On JavaRanch
Next week I'll be discussing my new book, Java EE 5 Development With NetBeans 6 in the IDEs, Version Control and other tools of the JavaRanch Big Moose Saloon.
I will be answering questions about the book, Java EE 5 and NetBeans.
See you there!
Posted at 09:10PM Oct 27, 2008 by David R. Heffelfinger in NetBeans | Comments[1]
NetBeans Book Title Changed
After some discussion with the publisher, we decided to change the title of my NetBeans book scheduled to be released very soon.
The new title is "Java EE 5 Development With NetBeans 6", the main reason for the change is that we wanted to have version numbers in the title, so that the title gives a better idea of exactly what is covered.
I would like to clarify that the book targets NetBeans 6.5, however, most of the material is applicable to NetBeans 6.0 and 6.1, therefore we decided to just state "NetBeans 6" in the title, since the material applies to any NetBeans 6.X version.
Posted at 07:12PM Oct 23, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
NetBeans 6.5 RC1 Released
NetBeans 6.5 RC1 available, it can be found at http://download.netbeans.org/netbeans/6.5/rc/.
My soon to be published book, Java EE Development With NetBeans, covers NetBeans 6.5.
Posted at 12:45PM Oct 22, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
Netbeans 6.5 RC1 to be released soon
The NetBeans Core QA Team has announced that NetBeans 6.5 RC1 will be released soon . It is expected to be available this afternoon.
I would like this opportunity to mention that my new NetBeans Book covers NetBeans 6.5.
Posted at 06:43PM Oct 17, 2008 by David R. Heffelfinger in NetBeans | Comments[2]
NetBeans IDE Turning 10 Years Old

The NetBeans IDE will turn 10 years old this month.
To celebrate, the NetBeans team is planning several activities. Check it out at http://www.netbeans.org/birthday/.
Posted at 07:35PM Oct 15, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
Automated DAO Generation from JPA Entities with NetBeans
One very nice NetBeans feature is the ability to generate Java Persistence API (JPA) entitites from an existing database schema. NetBeans can also generate complete JSF CRUD applications from JPA entities in a project. Both of these features are covered in detail in my Java EE 5 Development With NetBeans book.
As of NetBeans 6.5, scheduled to be released next month, will also add the ability to automatically generate DAO's (NetBeans calls them controllers, but they are, in fact, Data Access Objects) from existing JPA Entities.

This will certainly save us developers a lot of work, here is a sample DAO from generated from the Customer JPA entity discussed in the book:
package com.ensode.customerdb.jpacontrollers;import com.ensode.customerdb.jpa.Customer;
import com.ensode.customerdb.jpacontrollers.exceptions.NonexistentEntityException;
import com.ensode.customerdb.jpacontrollers.exceptions.PreexistingEntityException;
import com.ensode.customerdb.jpacontrollers.exceptions.RollbackFailureException;
import java.util.List;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import com.ensode.customerdb.jpa.CustomerOrder;
import java.util.ArrayList;
import java.util.Collection;
import com.ensode.customerdb.jpa.Address;
import com.ensode.customerdb.jpa.Telephone;
import javax.transaction.UserTransaction;/**
*
* @author heffel
*/
public class CustomerJpaController {
@Resource
private UserTransaction utx = null;
@PersistenceUnit(unitName = "WebApplication1PU")
private EntityManagerFactory emf = null;public EntityManager getEntityManager() {
return emf.createEntityManager();
}public void create(Customer customer) throws PreexistingEntityException, RollbackFailureException, Exception {
if (customer.getCustomerOrderCollection() == null) {
customer.setCustomerOrderCollection(new ArrayList());
}
if (customer.getAddressCollection() == null) {
customer.setAddressCollection(new ArrayList());
}
if (customer.getTelephoneCollection() == null) {
customer.setTelephoneCollection(new ArrayList());
}
EntityManager em = null;
try {
utx.begin();
em = getEntityManager();
CollectionattachedCustomerOrderCollection = new ArrayList ();
for (CustomerOrder customerOrderCollectionCustomerOrderToAttach : customer.getCustomerOrderCollection()) {
customerOrderCollectionCustomerOrderToAttach = em.getReference(customerOrderCollectionCustomerOrderToAttach.getClass(), customerOrderCollectionCustomerOrderToAttach.getCustomerOrderId());
attachedCustomerOrderCollection.add(customerOrderCollectionCustomerOrderToAttach);
}
customer.setCustomerOrderCollection(attachedCustomerOrderCollection);
Collection attachedAddressCollection = new ArrayList();
for (Address addressCollectionAddressToAttach : customer.getAddressCollection()) {
addressCollectionAddressToAttach = em.getReference(addressCollectionAddressToAttach.getClass(), addressCollectionAddressToAttach.getAddressId());
attachedAddressCollection.add(addressCollectionAddressToAttach);
}
customer.setAddressCollection(attachedAddressCollection);
CollectionattachedTelephoneCollection = new ArrayList ();
for (Telephone telephoneCollectionTelephoneToAttach : customer.getTelephoneCollection()) {
telephoneCollectionTelephoneToAttach = em.getReference(telephoneCollectionTelephoneToAttach.getClass(), telephoneCollectionTelephoneToAttach.getTelephoneId());
attachedTelephoneCollection.add(telephoneCollectionTelephoneToAttach);
}
customer.setTelephoneCollection(attachedTelephoneCollection);
em.persist(customer);
for (CustomerOrder customerOrderCollectionCustomerOrder : customer.getCustomerOrderCollection()) {
Customer oldCustomerIdOfCustomerOrderCollectionCustomerOrder = customerOrderCollectionCustomerOrder.getCustomerId();
customerOrderCollectionCustomerOrder.setCustomerId(customer);
customerOrderCollectionCustomerOrder = em.merge(customerOrderCollectionCustomerOrder);
if (oldCustomerIdOfCustomerOrderCollectionCustomerOrder != null) {
oldCustomerIdOfCustomerOrderCollectionCustomerOrder.getCustomerOrderCollection().remove(customerOrderCollectionCustomerOrder);
oldCustomerIdOfCustomerOrderCollectionCustomerOrder = em.merge(oldCustomerIdOfCustomerOrderCollectionCustomerOrder);
}
}
for (Address addressCollectionAddress : customer.getAddressCollection()) {
Customer oldCustomerIdOfAddressCollectionAddress = addressCollectionAddress.getCustomerId();
addressCollectionAddress.setCustomerId(customer);
addressCollectionAddress = em.merge(addressCollectionAddress);
if (oldCustomerIdOfAddressCollectionAddress != null) {
oldCustomerIdOfAddressCollectionAddress.getAddressCollection().remove(addressCollectionAddress);
oldCustomerIdOfAddressCollectionAddress = em.merge(oldCustomerIdOfAddressCollectionAddress);
}
}
for (Telephone telephoneCollectionTelephone : customer.getTelephoneCollection()) {
Customer oldCustomerIdOfTelephoneCollectionTelephone = telephoneCollectionTelephone.getCustomerId();
telephoneCollectionTelephone.setCustomerId(customer);
telephoneCollectionTelephone = em.merge(telephoneCollectionTelephone);
if (oldCustomerIdOfTelephoneCollectionTelephone != null) {
oldCustomerIdOfTelephoneCollectionTelephone.getTelephoneCollection().remove(telephoneCollectionTelephone);
oldCustomerIdOfTelephoneCollectionTelephone = em.merge(oldCustomerIdOfTelephoneCollectionTelephone);
}
}
utx.commit();
} catch (Exception ex) {
try {
utx.rollback();
} catch (Exception re) {
throw new RollbackFailureException("An error occurred attempting to roll back the transaction.", re);
}
if (findCustomer(customer.getCustomerId()) != null) {
throw new PreexistingEntityException("Customer " + customer + " already exists.", ex);
}
throw ex;
} finally {
if (em != null) {
em.close();
}
}
}public void edit(Customer customer) throws NonexistentEntityException, RollbackFailureException, Exception {
EntityManager em = null;
try {
utx.begin();
em = getEntityManager();
Customer persistentCustomer = em.find(Customer.class, customer.getCustomerId());
CollectioncustomerOrderCollectionOld = persistentCustomer.getCustomerOrderCollection();
CollectioncustomerOrderCollectionNew = customer.getCustomerOrderCollection();
Collection addressCollectionOld = persistentCustomer.getAddressCollection();
Collection addressCollectionNew = customer.getAddressCollection();
CollectiontelephoneCollectionOld = persistentCustomer.getTelephoneCollection();
CollectiontelephoneCollectionNew = customer.getTelephoneCollection();
CollectionattachedCustomerOrderCollectionNew = new ArrayList ();
for (CustomerOrder customerOrderCollectionNewCustomerOrderToAttach : customerOrderCollectionNew) {
customerOrderCollectionNewCustomerOrderToAttach = em.getReference(customerOrderCollectionNewCustomerOrderToAttach.getClass(), customerOrderCollectionNewCustomerOrderToAttach.getCustomerOrderId());
attachedCustomerOrderCollectionNew.add(customerOrderCollectionNewCustomerOrderToAttach);
}
customerOrderCollectionNew = attachedCustomerOrderCollectionNew;
customer.setCustomerOrderCollection(customerOrderCollectionNew);
Collection attachedAddressCollectionNew = new ArrayList();
for (Address addressCollectionNewAddressToAttach : addressCollectionNew) {
addressCollectionNewAddressToAttach = em.getReference(addressCollectionNewAddressToAttach.getClass(), addressCollectionNewAddressToAttach.getAddressId());
attachedAddressCollectionNew.add(addressCollectionNewAddressToAttach);
}
addressCollectionNew = attachedAddressCollectionNew;
customer.setAddressCollection(addressCollectionNew);
CollectionattachedTelephoneCollectionNew = new ArrayList ();
for (Telephone telephoneCollectionNewTelephoneToAttach : telephoneCollectionNew) {
telephoneCollectionNewTelephoneToAttach = em.getReference(telephoneCollectionNewTelephoneToAttach.getClass(), telephoneCollectionNewTelephoneToAttach.getTelephoneId());
attachedTelephoneCollectionNew.add(telephoneCollectionNewTelephoneToAttach);
}
telephoneCollectionNew = attachedTelephoneCollectionNew;
customer.setTelephoneCollection(telephoneCollectionNew);
customer = em.merge(customer);
for (CustomerOrder customerOrderCollectionOldCustomerOrder : customerOrderCollectionOld) {
if (!customerOrderCollectionNew.contains(customerOrderCollectionOldCustomerOrder)) {
customerOrderCollectionOldCustomerOrder.setCustomerId(null);
customerOrderCollectionOldCustomerOrder = em.merge(customerOrderCollectionOldCustomerOrder);
}
}
for (CustomerOrder customerOrderCollectionNewCustomerOrder : customerOrderCollectionNew) {
if (!customerOrderCollectionOld.contains(customerOrderCollectionNewCustomerOrder)) {
Customer oldCustomerIdOfCustomerOrderCollectionNewCustomerOrder = customerOrderCollectionNewCustomerOrder.getCustomerId();
customerOrderCollectionNewCustomerOrder.setCustomerId(customer);
customerOrderCollectionNewCustomerOrder = em.merge(customerOrderCollectionNewCustomerOrder);
if (oldCustomerIdOfCustomerOrderCollectionNewCustomerOrder != null && !oldCustomerIdOfCustomerOrderCollectionNewCustomerOrder.equals(customer)) {
oldCustomerIdOfCustomerOrderCollectionNewCustomerOrder.getCustomerOrderCollection().remove(customerOrderCollectionNewCustomerOrder);
oldCustomerIdOfCustomerOrderCollectionNewCustomerOrder = em.merge(oldCustomerIdOfCustomerOrderCollectionNewCustomerOrder);
}
}
}
for (Address addressCollectionOldAddress : addressCollectionOld) {
if (!addressCollectionNew.contains(addressCollectionOldAddress)) {
addressCollectionOldAddress.setCustomerId(null);
addressCollectionOldAddress = em.merge(addressCollectionOldAddress);
}
}
for (Address addressCollectionNewAddress : addressCollectionNew) {
if (!addressCollectionOld.contains(addressCollectionNewAddress)) {
Customer oldCustomerIdOfAddressCollectionNewAddress = addressCollectionNewAddress.getCustomerId();
addressCollectionNewAddress.setCustomerId(customer);
addressCollectionNewAddress = em.merge(addressCollectionNewAddress);
if (oldCustomerIdOfAddressCollectionNewAddress != null && !oldCustomerIdOfAddressCollectionNewAddress.equals(customer)) {
oldCustomerIdOfAddressCollectionNewAddress.getAddressCollection().remove(addressCollectionNewAddress);
oldCustomerIdOfAddressCollectionNewAddress = em.merge(oldCustomerIdOfAddressCollectionNewAddress);
}
}
}
for (Telephone telephoneCollectionOldTelephone : telephoneCollectionOld) {
if (!telephoneCollectionNew.contains(telephoneCollectionOldTelephone)) {
telephoneCollectionOldTelephone.setCustomerId(null);
telephoneCollectionOldTelephone = em.merge(telephoneCollectionOldTelephone);
}
}
for (Telephone telephoneCollectionNewTelephone : telephoneCollectionNew) {
if (!telephoneCollectionOld.contains(telephoneCollectionNewTelephone)) {
Customer oldCustomerIdOfTelephoneCollectionNewTelephone = telephoneCollectionNewTelephone.getCustomerId();
telephoneCollectionNewTelephone.setCustomerId(customer);
telephoneCollectionNewTelephone = em.merge(telephoneCollectionNewTelephone);
if (oldCustomerIdOfTelephoneCollectionNewTelephone != null && !oldCustomerIdOfTelephoneCollectionNewTelephone.equals(customer)) {
oldCustomerIdOfTelephoneCollectionNewTelephone.getTelephoneCollection().remove(telephoneCollectionNewTelephone);
oldCustomerIdOfTelephoneCollectionNewTelephone = em.merge(oldCustomerIdOfTelephoneCollectionNewTelephone);
}
}
}
utx.commit();
} catch (Exception ex) {
try {
utx.rollback();
} catch (Exception re) {
throw new RollbackFailureException("An error occurred attempting to roll back the transaction.", re);
}
String msg = ex.getLocalizedMessage();
if (msg == null || msg.length() == 0) {
Integer id = customer.getCustomerId();
if (findCustomer(id) == null) {
throw new NonexistentEntityException("The customer with id " + id + " no longer exists.");
}
}
throw ex;
} finally {
if (em != null) {
em.close();
}
}
}public void destroy(Integer id) throws NonexistentEntityException, RollbackFailureException, Exception {
EntityManager em = null;
try {
utx.begin();
em = getEntityManager();
Customer customer;
try {
customer = em.getReference(Customer.class, id);
customer.getCustomerId();
} catch (EntityNotFoundException enfe) {
throw new NonexistentEntityException("The customer with id " + id + " no longer exists.", enfe);
}
CollectioncustomerOrderCollection = customer.getCustomerOrderCollection();
for (CustomerOrder customerOrderCollectionCustomerOrder : customerOrderCollection) {
customerOrderCollectionCustomerOrder.setCustomerId(null);
customerOrderCollectionCustomerOrder = em.merge(customerOrderCollectionCustomerOrder);
}
Collection addressCollection = customer.getAddressCollection();
for (Address addressCollectionAddress : addressCollection) {
addressCollectionAddress.setCustomerId(null);
addressCollectionAddress = em.merge(addressCollectionAddress);
}
CollectiontelephoneCollection = customer.getTelephoneCollection();
for (Telephone telephoneCollectionTelephone : telephoneCollection) {
telephoneCollectionTelephone.setCustomerId(null);
telephoneCollectionTelephone = em.merge(telephoneCollectionTelephone);
}
em.remove(customer);
utx.commit();
} catch (Exception ex) {
try {
utx.rollback();
} catch (Exception re) {
throw new RollbackFailureException("An error occurred attempting to roll back the transaction.", re);
}
throw ex;
} finally {
if (em != null) {
em.close();
}
}
}public List
findCustomerEntities() {
return findCustomerEntities(true, -1, -1);
}public List
findCustomerEntities(int maxResults, int firstResult) {
return findCustomerEntities(false, maxResults, firstResult);
}private List
findCustomerEntities(boolean all, int maxResults, int firstResult) {
EntityManager em = getEntityManager();
try {
Query q = em.createQuery("select object(o) from Customer as o");
if (!all) {
q.setMaxResults(maxResults);
q.setFirstResult(firstResult);
}
return q.getResultList();
} finally {
em.close();
}
}public Customer findCustomer(Integer id) {
EntityManager em = getEntityManager();
try {
return em.find(Customer.class, id);
} finally {
em.close();
}
}public int getCustomerCount() {
EntityManager em = getEntityManager();
try {
return ((Long) em.createQuery("select count(o) from Customer as o").getSingleResult()).intValue();
} finally {
em.close();
}
}}
Isn't it great to get all this code generated? No need to develop and debug it ourselves, letting us focus on implementing business logic.
Posted at 07:53PM Oct 13, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
NetBeans Book Almost Ready
We are working on the final touches of my new Netbeans book. If everything goes well, it should be published later this month.
Today I received "prefinal" versions of the first few chapters. Prefinal versions are what the publisher will sent to the printers once the book is completed. They are sent to the author for a final inspection and "sanity check" before having the book printed.
I'm very excited about this book. NetBeans is such a nice IDE and I hope my book can help others learn how to use it to its full potential.
Posted at 06:14PM Oct 10, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
New NetBeans Book Available for Pre-Order
I am pleased to announce that my new book, Java EE Development With NetBeans, is now available for pre-order.
Posted at 04:30PM Oct 06, 2008 by David R. Heffelfinger in NetBeans | Comments[0]
