Who’s On Phirst

Official blog of Phurnace Software.

Category >> Phurnace Tips

Posted by: Shawn Spiars on

From time to time I visit the Eclipse news groups to find a solution to a problem I am having with a particular area of Eclipse. Since Eclipse is such a huge platform with many projects there are many different news groups, each designed to cover a particular project or API (Application Programming Interface).

Some of my favorite Eclipse news groups are:

eclipse.platform
eclipse.platform.rcp
eclipse.platform.swt

Often while I am looking for answers in a news group I run across questions that I have already conquered and so I try to give back and help others. Recently I responded to a question about how to perform in-line editing within the cell of an SWT (Standard Widget Toolkit) table. I have included my example java program below. If you are a Java developer and somewhat familiar with SWT/JFace then you should have no problems understanding the example code. If you are new to SWT/JFace I have provided a brief summary of the primary components used to create the example.

Looking at the java code below you will see that the program contains a main method to launch the java program. The primary UI component is an SWT Table which is added to an SWT Composite. Under the table I have added SWT buttons to “Add” and “Remove” rows from the table. The SelectionListeners on the buttons handle the add and remove actions. The model for the table is a POJO (Plain Old Java Object) which I have named “Row”. A TableContentProvider and TableLabelProvider provide the content and labels for the table and the TextCellEditor provides the ability to edit the table allowing a user to type data directly into the table’s cells.


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

/**
* This is an example program showing how to use a TableCellEditor
* to allow direct editing of a table's cell.
*
* @author Shawn Spiars
*
*/

public class TableCellModifierExample {

private Table table;
private TableViewer tableViewer;

private final static String[] COLUMN_HEADINGS = {"Property",
"Value"}
;

public static void main(String [] args) {
new TableCellModifierExample();
}

public TableCellModifierExample() {
Display display = new Display();
Shell shell = new Shell(display, SWT.SHELL_TRIM);
shell.setText("TableCellModifier Example");
shell.setLayout(new FillLayout());

createContents(shell);

shell.setSize(400, 400);
shell.open();

while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}

protected Control createContents(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(1, false);
layout.verticalSpacing = 10;
composite.setLayout(layout);
GridData data = new GridData(GridData.FILL_BOTH);
data.grabExcessHorizontalSpace = true;
composite.setLayoutData(data);

table = new Table(composite, SWT.BORDER | SWT.V_SCROLL
| SWT.MULTI | SWT.FULL_SELECTION);
table.setLinesVisible(true);
table.setHeaderVisible(true);
data = new GridData(SWT.FILL, SWT.FILL, true, false);
data.heightHint = 300;
table.setLayoutData(data);

TableLayout tableLayout = new TableLayout();
table.setLayout(tableLayout);

tableLayout.addColumnData(new ColumnWeightData(10, 100,
true));
TableColumn column = new TableColumn(table, SWT.NONE);
column.setText(COLUMN_HEADINGS[0]);
column.setAlignment(SWT.LEFT);

tableLayout.addColumnData(new ColumnWeightData(15, 200,
true));
column = new TableColumn(table, SWT.NONE);
column.setText(COLUMN_HEADINGS[1]);
column.setAlignment(SWT.LEFT);

tableViewer = new TableViewer(table);
tableViewer.setColumnProperties(COLUMN_HEADINGS);
tableViewer.setContentProvider(new
TableContentProvider());
tableViewer.setLabelProvider(new TableLabelProvider());

CellEditor[] editors = new CellEditor[2];
editors[0] = new TextCellEditor(table);
editors[1] = new TextCellEditor(table);
tableViewer.setCellEditors(editors);
tableViewer.setCellModifier(new TableCellModifier());

Composite buttonComposite = new Composite(composite,
SWT.NONE);
FillLayout fillLayout = new FillLayout(SWT.HORIZONTAL);
fillLayout.spacing = 10;
buttonComposite.setLayout(fillLayout);

Button addButton = new Button(buttonComposite,
SWT.PUSH);
addButton.setText("Add");
addButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Row row = new Row("", "");
tableViewer.add(row);
table.setTopIndex(table.getItemCount());
table.select(table.getItemCount()-1);
tableViewer.editElement(row, 0);
}
});

Button removeButton = new Button(buttonComposite,
SWT.PUSH);
removeButton.setText("Remove");
removeButton.addSelectionListener(new
SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
ISelection selection =
tableViewer.getSelection();
if (selection instanceof
IStructuredSelection) {
Iterator iterator =
((IStructuredSelection)selection).iterator();
while(iterator.hasNext()) {
Object obj = iterator.next();
tableViewer.remove(obj);
}
}
}
});

return composite;
}

public class TableLabelProvider extends LabelProvider
implements
ITableLabelProvider {
public Image getColumnImage(Object element, int
columnIndex) {
return null;
}

public String getColumnText(Object element,
int columnIndex) {
Row row = (Row) element;

switch
(columnIndex) {
case 0:
return row.getKey();
case 1:
return row.getValue();
}

return null;
}

}

public class TableContentProvider implements
IStructuredContentProvider {
public Object[] getElements(Object parent) {
List results = new ArrayList();
if (parent instanceof ArrayList) {
results = (ArrayList) parent;
}
return results.toArray();
}

public void dispose() {
}

public void inputChanged(Viewer viewer, Object oldInput,
Object newInput) {
}

}

class TableCellModifier implements ICellModifier {

public boolean canModify(Object element,
String property) {
return true;
}

public Object getValue(Object element,
String property) {
Object result = null;

Row row = (Row) element;

List list = Arrays.asList(COLUMN_HEADINGS);
int columnIndex = list.indexOf(property);

switch (columnIndex) {
case 0:
result = row.getKey();
break;
case 1:
result = row.getValue();
break;
}

return result;
}

public void modify(Object element, String property,
Object value) {
List list = Arrays.asList(COLUMN_HEADINGS);
int columnIndex = list.indexOf(property);

TableItem tableItem = (TableItem) element;
Row row = (Row) tableItem.getData();

switch (columnIndex) {
case 0:
String key = (String) value;
if (key.length() > 0) {
row.setKey(key);
}
break;

case 1:
String v = (String) value;
if (v.length() > 0) {
row.setValue(v);
}
break;
}
tableViewer.update(row, null);
}

}

private class Row {
private String key;
private String value;

public Row(String key, String value) {
setKey(key);
setValue(value);
}

public void setKey(String key) {
this.key = key
}

public String getKey() {
return key;
}

public void setValue(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}

}

In SWTJFaceSWTStandard Widget ToolkitEclipse
Comment (0) Read More...


Posted by: Shawn Spiars on

It seems like there are more visual GUI builder products available on the market today than ever before. Visual GUI builders propose an easier way to design and create user interface components. They usually consist of a palette from which you select and drag controls or widgets onto a window, frame, or dialog. Then they provide a table or list where you can manipulate the properties of your controls (size, font, color, coordinates, etc) without requiring the user to know the subtleties’ of the syntax or layout managers. Here is a really good link if you are interested in Java GUI Builders.

Call me old fashion, but I always prefer a language editor (HTML, XML, Java, etc…) over a visual editor because I want to understand the commands I write, and I want to format and organize the code in a way that is meaningful to me so I can easily edit the code later. A good language editor will have syntax highlighting, syntax checking, command completion and various other features to assist you in developing the code, but it won’t generate the substance of the code for you.

Another type of code generation tool falls into the Model Driven Development (MDA) category. With MDD, logical models are used to capture design decisions and generate code, and sometimes the generated code is user interface code. The problem I have with user interfaces generated from models is that there is no consideration by the machine for who the user is, their technical aptitude, or the way they approach a task (workflow). The entire human experience is missing and the user interface has been reduced to a bunch of domain objects and business rules.

For example, the Eclipse Modeling Framework (EMF) can use a model to quickly generate a cool looking prototype consisting of a relational tree, object listeners, pop-up menus, and a properties view, but this in no way resembles a complex, well designed, user friendly application. And don’t be fooled into thinking you can easily tweak the generated code to get it to behave the way you want it to. I’ve spent hours and hours digging thru layers and layers of cryptic machine generated code trying to find the one line that needs to be changed to get the desired behavior.

There are plenty of MDD advocates out there who will disagree with me. Most of them are experts in modeling, but I’ve never met one who is an expert in user interface design. Since most project leads and architects today are very familiar with UML and modeling tools they often buy into the promise that MDD will save them time on their UI development. This has not been my experience.

If you do choose to use a modeling tool to generate UI code – I recommend you decouple the UI model from the back-end model. Just imagine a project where every time the back-end architect decides to change a relationship in the model the UI mysteriously breaks with no warnings.

“And that’s all I have to say about that.”

-Shawn

In Java GUIEclipse
Comment (0) Read More...


Posted by: Pete Pickerill on

In my last post I talked about how virtualization changed test lab creation and management. I’m sure the first thing you did after reading the post was download your very own copy of VMWare Server and replace all your hardware with super flexible virtual machines. You’re now basking in the glow of your long term cost savings, marveling at how easy your lab is to maintain, and using some of that extra time and money to bid on my exclusive line of “VMWare Saved My Marriage” T-Shirts available only at my eBay store.

Eventually your boss will notice you’re no longer cursing under your breath and slamming the door to the lab. It’s inescapable. And when the boss man gets wind of spare cycles, he starts thinking crazy thoughts. He starts thinking you now have time for that automation project that’s been on the back burner since 1996. With a heavy sigh and a final farewell to what will inevitably be known as “The period in my life that was great, thanks to Pete Pickerill,” you turn your attention to how you’re going to build a flexible automation framework that runs just as well on your Unix & Linux systems as it does on your Windows systems.

Have I got an open-source project for you! Software Test Automation Framework (STAF) takes care of all the nagging little odds and ends that have very little to do with testing your software. STAF employs a series of pluggable modules (called ‘Services’). Standard services cover most of what you’ll need: file system operations, environment variable management, process execution, logging, and miscellaneous commands like ‘ping’ and ‘zip.’

# STAF Server1 FS COPY FILE myFile.exe TODIRECTORY /your/dir/here TOMACHINE System2

The command above may seem a little cryptic, but when you break it down into steps it starts to make sense. In the example above:

  • STAF is executed on Server1: STAF Server1
  • The Filesystem Service (FS) is invoked to copy the file myFile.exe: FS COPY FILE myFile.exe
  • The target directory is given: TODIRECTORY /your/dir/here
  • The target machine is given: TOMACHINE System2

That’s STAF in a nutshell. You execute the STAF command locally or remotely, you specify the service you want to use, what you want to execute within that service, and provide the necessary variables to complete the task.

Now you may be thinking, “But, Pete! What if the copy fails due to disk space limitations or network interruptions? What if System2 didn’t boot to a usable state? What if it just up and fails for no discernible reason at all?” There, there buddy! It’s alright! STAF gives you very descriptive return codes. What’s more, these can either be returned to STDOUT if you’re using the cli or a scripting interface or as Java Objects if you’re using STAF in a Java class.

Other cool things about STAF:

  • Accessible: Java, Perl, Python, Tcl APIS and an Ant task make it very easy to fold STAF into your automation. There’s also an Eclipse plug-in I haven’t had a chance to check out.
  • Extensible: Write your own services in Java, C, or C++ to handle testing peculiarities specific to your software.
  • Documentation: STAF is managed by IBM and the documentation is the best I’ve ever come across in the Open Source realm. STAF DOCS
  • Active Community: STAF is frequently updated and new features and integrations are added all the time. There’s also loads of info in the mailing list. STAF MAILING LISTS & DISCUSSION FORUM

For more examples and info, check out the STAF website, Agile Testing Blog & Performance Wiki.

In TipsSTAFSoftware Test Automation FrameworkOpen Sourceagile testing
Comment (0) Read More...


Posted by: Robert Reeves on

When you buy a software tool for your use, you aren’t just buying the application; you are buying the BENEFITS of what it does. Most often, you are really buying piano recitals, little league games and happy hours with your friends. You buy yourself time when you buy a good tool. Because with the right tools, you are in bed, fast asleep, instead of the datacenter at 3 a.m. pulling out your hair because of a deployment error or a server that won’t capture transactions, or whatever. With good system administration tools you get to tackle the hard problems and challenges that made you take your job in the first place instead of the mundane, boring and error-prone tasks.

Unfortunately, “quality of life” has never been a valuable selling point for IT managers and directors. They pay you to keep the network running, deploy applications and configure application servers. The manner in which you perform your job doesn’t matter to them; only the results.

If you aren’t using the right tools, then a huge proportion of your production failures are due to configuration errors. Thus, your entire management team should care about how you perform your job, just like you do.

Simply put, proving a fast ROI is the best way to make a business case for a software purchase and it is terribly simple. ROI, or Return On Investment, is a simple calculation that states the rate offered by an investment. So, if you invest $1000 that provides you a return of $500 per year, then you have a 50% ROI per year ($500/$1000 = 50% ROI). In other words, the “pay back” period for the investment is 2 years ($500 per year, for 2 years = $1000). The rule of thumb in IT spending is that investments need to have a “pay back” of 3 years or less. So to invest $1000, you need to show that you will get more than $333 back per year.

With many tools (could be OpsWare, Build Forge, Phurnace Deliver …), you are provided with a way to avoid costly mistakes and down time. But, determining the dollar amount of those mistakes and downtime can be difficult. Luckily, many vendors (us included) provide an online calculator to help determine those values. And, when you are done filling out the web form, you can create a PDF of your cost analysis to email to your boss.

Recently, one or our customers determined their deployment costs to be $68,365.38 per year and the cost of error resolution was $171,000 per year. That’s a total cost of $239,365.38 per year. To determine their ROI, they simply divided the money saved by our product’s cost. They arrived at a monster 3 digit ROI number and were very happy with their purchase.

Of course, you can always rely on your friendly local software sales rep (like the Phurnace Account Manager) to help you with the purchase process. We all want you to be successful and tackle the truly challenging aspects of your job. So, leave the mundane, boring error-prone stuff to the tool vendors.

In deliver
Comment (0) Read More...


Posted by: Daniel Nelson on

If you have never been in the datacenter in the wee hours of the morning, trying to figure out what's wrong with a server, stop reading this.  If you have never been in an all-hands-on-deck meeting where everyone is trying to figure out what went wrong with a deployment, then stop right now and go do something more useful with your time.  Because if you haven't lived through that, then the rest of this post won't mean much to you.

I've been through plenty of those times.  And the truth is no matter what company I worked at - big, small, or something in between - we always had those times.  They are just a fact of life for people whose job it is to keep the server up and running.

Which brings me to the point of this entry (which is naughty blogging of me - you are supposed to make your point in the first paragraph, but oh well.  It's my blog, so I get to do what I want).  Anyhoo - the point is that so much is wrong with how we think about the "Software Development Lifecycle."  In all the flow charts and diagrams I've seen they all seem to be ignoring the very basic point that things go wrong.  And they don't have a lot of built-in mechanisms to help you through that phase of the life-cycle.  It's a black box in the flow chart that says "insert troubleshooting here."

Let me back up a bit.  If nothing changes about a server environment, chances are you aren't going to have a lot of problems.  Maybe a power supply goes out.  Maybe the network drops.  But for the most part you are only going to have problems when things change.  And that's the catch 22.  Things are supposed to change.  New code is supposed to go out to the servers.  New features are supposed to make it into the hands of eager customers.  And those changes are supposed to happen more often rather than less frequently.  That's how our companies compete with each other - it's called time to market.

And that's the fundamental disconnect between the two goals of keeping things up and running, and getting new code out faster.  As diplomat Henry Kissinger used to say, "real tragedy doesn't happen when right faces wrong, but rather when two rights face each other".  Keeping the servers up is a good thing.  Getting code out faster is a good thing.  How do you balance the two?

To me, any real Software Development Life-cycle has to fully embrace the fundamental belief that things are going to go wrong during any process.  Phurnace Software sells a product that helps do things better, but I can't claim, and I wouldn't claim, that if you use Phurnace to do your deployments that you will never have a problem again.  That's ludicrous, and simply not credible to people who have done this as long as I have.

But one thing that is very different about Phurnace, and the way we do things, is that what we have built is functionality that is designed to specifically help you out when things do go wrong.  We know it's going to happen at some point, and we work hard to try to make it as infrequent as humanly possible.  But it will happen.  And when it does, we have the built-in the features you will need to figure that part out.  Heck, we even have a product named Troubleshoot. 

So, when you are looking at how to balance that "time to market" with "keep the servers up", keep in mind that you have to have robust tools and process in place to fix the problem.   A black box just doesn't cut it.

In troubleshootTipsjava
Comment (0) Read More...