Who’s On Phirst

Official blog of Phurnace Software.

Archive >> December 2008

Posted by: Cynthia Sadler on

When designing your own custom WebSphere Portal themes and skins, after initially creating them in the Portal Admin Console and in the file system of your WebSphere Application Server, it is important to update your WebSphere Portal EAR file with your new themes and skins. Otherwise, your new themes and skins can be overwritten or deleted whenever the WebSphere Portal EAR is updated. So, what do we need to do to add our shiny new custom themes and skins to the WebSphere Portal EAR file? Unfortunately, this involves a little bit of scripting with wsadmin and EARExpander. This is all documented in the IBM online help. If you don't want to do this manually every time, you end up with a shell script for Linux or Cygwin (or similar DOS batch file) that looks something like this to update a new skin called qaThinSkin and a new theme called qaIBM:



This can be quite tedious and error-prone (and subsequently, costly) if you are constantly tweaking your skins and themes and need to move them from QA to production. This is where Phurnace WebSphere Portal Deliver can help. After you have initially created your custom skin and theme, Deliver can snapshot your WebSphere Portal configuration. Then we can use the Portal Configuration Packager Wizard to pare down the configuration to just the custom skin and theme.



Then copy your custom theme and skin to your Deliver client, keeping the same directory structure as they would be under the wps.war directory on your WebSphere Application Server:



Next we add the local directory for our themes and skins to the Deliver server profile, in the Portal tab.



Now we can make updates to the JSPs or GIFs on our Deliver client and then do a Portal Install to the WebSphere Portal application server to see the updates. You can even use our Portal Copy feature to transfer your custom themes and skins from your QA environment to your production environment. With no more time spent scripting, you can actually use your time for more important things like designing your custom skins and themes, and let Phurnace WebSphere Portal Deliver do all the deployment work for you.

In WebSphere Portal
Comment (0) Read More...


Posted by: Shawn Spiars on

Before we get started here are a couple of definitions that may help you understand this example.

  • A wizard is a series of pages that guide a user through a complex task.
  • JFace is a user interface framework that is used to handle many common UI programming tasks.

JFace provides all the user interface components needed to create a wizard for your application. Most of the time when designing a wizard you will know at design time how many pages are required to complete each step of your wizard. But on occasion you will run across a use case where you need to create pages dynamically based upon the user’s input from a previous page. Solving this problem with JFace is a little tricky, so I’ve created an example program demonstrating how to dynamically create wizard pages based upon the input from the first page. Give it a try and let me know what you think.


import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Display;
import org.eclipse.swt.widgets.Shell;

/**
 * Example program demonstrating how to dynamically add pages to a JFace Wizard.
 *
 * @author Shawn Spiars 
 * 
 */ 
public class DynamicWizardPageExample {

      private static class MyWizard extends Wizard {
            public MyWizard() {
                  super();
                  setForcePreviousAndNextButtons(true);
            }

            public void addPages() {
                  addPage(new StartingWizardPage());
            }

            public boolean performFinish() {
                  return true;
            }

            @Override
            public IWizardPage getNextPage(IWizardPage page) {
                  IWizardPage nextPage = super.getNextPage(page);
                  
                  //TODO - create logic here to skip pages that you
                  //have added to the framework, but no longer wish to display
                  
                  return nextPage;
            }

      };

      private static class StartingWizardPage extends WizardPage {
            private Button firstPageButton;
            private Button secondPageButton;
            private Button thirdPageButton;

            protected StartingWizardPage() {
                  super("startingPage");
                  setTitle("Starting Page");
              setMessage("Select the desired pages and press the Next button.");
            }

            public void createControl(Composite parent) {
                  Composite composite = new Composite(parent, SWT.NONE);
              GridLayout layout = new GridLayout(1, false);
              layout.verticalSpacing = 12;
              composite.setLayout(layout);
              composite.setLayoutData(new GridData(GridData.FILL_BOTH));
              
              firstPageButton = new Button(composite, SWT.CHECK);
              firstPageButton.setText("Page One");
              firstPageButton.addSelectionListener(new SelectionAdapter() {
                        public void widgetSelected(SelectionEvent e) {
                              Wizard wizard = (Wizard) getWizard();
                              wizard.addPage(new MyWizardPage("Page One"));
                              getContainer().updateButtons();
                        }
                  });
              
              secondPageButton = new Button(composite, SWT.CHECK);
              secondPageButton.setText("Page Two");
              secondPageButton.addSelectionListener(new SelectionAdapter() {
                        public void widgetSelected(SelectionEvent e) {

                              Wizard wizard = (Wizard) getWizard();
                              wizard.addPage(new MyWizardPage("Page Two"));
                              getContainer().updateButtons();
                        }
                  });
              
              thirdPageButton = new Button(composite, SWT.CHECK);
              thirdPageButton.setText("Page Three");
              thirdPageButton.addSelectionListener(new SelectionAdapter() {
                        public void widgetSelected(SelectionEvent e) {
                              Wizard wizard = (Wizard) getWizard();
                              wizard.addPage(new MyWizardPage("Page Three"));
                              getContainer().updateButtons();
                        }
                  });
              
              setControl(composite);
            }
            
      }
      
      private static class MyWizardPage extends WizardPage {
            protected MyWizardPage(String pageName) {
                  super(pageName);
                  setTitle(pageName);
            }

            public void createControl(Composite parent) {
                  Composite composite = new Composite(parent, SWT.NONE);
                  setControl(composite);
            }
      }

      public static void main(String[] args) {
            Display display = new Display();

            final Shell shell = new Shell(display);
            shell.setLayout(new FillLayout());

            Button b = new Button(shell, SWT.PUSH);
            b.setText("Press here to open wizard");
            b.addSelectionListener(new SelectionAdapter() {
                  public void widgetSelected(SelectionEvent e) {
                        WizardDialog dialog = new WizardDialog(shell, new MyWizard());
                        dialog.open();
                  }
            });

            shell.open();

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

            display.dispose();
      }
}


For more information on JFace, check out these links:

In Untagged 
Comment (0) Read More...


Posted by: Ann Nguyen on

In the world of fast paced development, you are usually tasked to deliver some features in a very short turnaround time. Does this sound familiar to you? Especially if you are in an IT shop, you find yourself more and more like an assembler, you just integrate and connect software together for the final product, and you have to rely on tools of the 21st century.

I was given, or rather volunteered, to pull data from one web application to display on a client’s web application remotely. The web application that provided the necessary data used EJB for all its CRUD operations. Instead of reinventing the wheel, I added more methods to the stateless session bean to expose the data that I needed.

First step, with EJB 2.1:
Axis is the best tool for this that I have found. Axis is packaged as another web module into your application and exposes the EJB methods as a Web Service.

The Axis server-config.wsdd needs to define the Web Service.
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="AdminService" provider="java:MSG">
<parameter name="allowedMethods" value="AdminService"/>
<parameter name="enableRemoteAdmin" value="false"/>
<parameter name="className" value="org.apache.axis.utils.Admin"/>
<namespace>http://xml.apache.org/axis/wsdd/</namespace>
<namespace>http://xml.apache.org/axis/wsdd/</namespace>
</service>
<service name="Version" provider="java:RPC">
<parameter name="allowedMethods" value="getVersion"/>
<parameter name="className" value="org.apache.axis.Version"/>
</service>
<service name="Verify" provider="java:RPC">
<parameter name="allowedMethods" value="getAxisVerification"/>
<parameter name="className" value="com.mycompany.dataserver.model.session.DataEJBService"/>
</service>
<handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>
<transport name="http">
<requestFlow>
<handler type="URLMapper"/>
</requestFlow>
</transport>
<service name="DataByPersonId" provider="java:EJB">
<parameter name="scope" value="application"/>
<parameter name="beanJndiName" value="java:comp/env/DataEJB"/>
<parameter name="homeInterfaceName" value="com.mycompany.dataserver.model.session.DataEJBLocal"/>
<parameter name="remoteInterfaceName" value="com.mycompany.dataserver.model.session.DataEJBLocal"/>
<parameter name="allowedMethods" value="getDataByPersonId,getDataByOrgId,getDataIDsByOrgId"/>
<parameter name="className" value="com.mycompany.dataserver.model.session.DataEJBBean"/>
<beanMapping xmlns:ns="urn:DataByPersonId.session.model.dataserver.mycompany.com" qname="ns:DataWsDTO"
languageSpecificType="java:com.mycompany.dataserver.model.datatransferobject.DataWsDTO"/>
</service>
</deployment>

Since the EJB did not support the remote interface, the web.xml for the Axis servlet needs to define the reference to the session bean.
    <ejb-local-ref>
<ejb-ref-name>DataEJB</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>com.mycompany.dataserver.model.session.DataEJBLocal</local>
<ejb-link>ListingsEJB</ejb-link>
</ejb-local-ref>

First step, with EJB 3:
Life is much easier with EJB 3. One just has to use some dependency injection keywords.

The DataEJBBean file:

@WebService(endpointInterface="com.mycompany.dataserver.model.session.DataEJBRemote",targetNamespace="...")
@Stateless(name="DataEJB")
@Remote(DataEJBRemote.class)
@TransactionManagement(TransactionManagementType.CONTAINER)
@Resource(name="jdbc/MyCompanyDataServerDS", type=javax.sql.DataSource.class)
@Deployment(contextPath="data-ejb")
public class DataEJBBean implements DataEJBLocal, DataEJBRemote {

@WebMethod(operationName="featuredData")
@TransactionAttribute (TransactionAttributeType.SUPPORTS)
public DataWsDTO[] getDataByPersonId(Integer personId) {
.
.
.
}

@WebMethod(operationName="featuredData")
@TransactionAttribute (TransactionAttributeType.SUPPORTS)
public String getDataIDsByOrgId(Integer orgId) {
.
.
.
}


@WebMethod(operationName="featuredData")
@TransactionAttribute (TransactionAttributeType.SUPPORTS)
public ListingWsDTO[] getDataByOrgId(Integer orgId) {
.
.
.
}

.
.
.

}
The DataEJBRemote file:


@WebService
public interface DataEJBRemote extends Remote {
@WebMethod
public DataWsDTO[] getDataByPersonId(Integer personId) throws java.rmi.RemoteException;

@WebMethod
public String getDataIDsByOrgId(Integer orgId) throws java.rmi.RemoteException;

@WebMethod
public DataWsDTO[] getDataByOrgId(Integer orgId) throws java.rmi.RemoteException;
}


For EJB 3 at this stage of the game, I would support the remote interface, especially for the Web Services features.

We are all familiar with the software life cycle story, the PM for that container release probably only schedules mainstream use cases into these earlier releases. If you like pain, and you do have the time, you can venture out more.

Second Step, obtaining the WSDL for the Web Service:
If you use Axis, you can use the ant task Java2WSDL to obtain the WSDL. You can also invoke the URL of your web service with "?wsdl" at the end to obtain the WSDL.

Third Step, generate the client stubs for the web service consumer:

I used Axis for this and generated the client stubs from the WSDL obtained in the second step. The client stubs will need to be package into the consumer web applications.

If you are like me, we all have to support the different staging steps: development, QA, and production. Each of the machines for these are different for both the server and the consumers and we only want to move the same ear through these steps of staging.

In the ...SoapBindingStub.java file that is generated by Axis, I modified the static method to pull the Web Service namespace from a properties file. The property file for development, QA, and production will need to reflect its environments.

public static String getDataWebServiceNamespace() {
return FileUtils.getDataWebServiceUrl();
}


Everywhere that the Web Service namespace is needed, use this method to obtain it.

Fourth Step, deployment night:
Deployment night is scheduled late in the evening. Most IT shops do this. After all of the work, the frustration, the hair pulling and frantic reading into Open Source documentation like Axis (we all just LOVE Open Source documentation...), it DID NOT work in the production environment.

So you are now troubleshooting late into the night and the whole deployment team is weary and tempers flare easily.

The deployment system admin person did not move the correct property file into the production environment. Well, it is not their fault totally, you did not spell out exactly what files need to move to where clear enough in the manual deployment instructions.

This is why you need to look into Phurnace Deliver, especially the upload file functionality. It will save you so much time - your time, not machine time, from erroneous manual mistakes.

In Untagged 
Comment (0) Read More...


Posted by: Robert Reeves on

Let me draw an analogy for you. I want to compare scripting in your IT shop to something near and dear to my heart.

I own over 75 feet of vinyl records, countless organizers that hold 500 CDs each and over a terabyte of MP3, AAC, FLAC and other alphabet soup files. My taste in music runs the gamut of early Jazz to Bop to Soul to Reggae to Punk Rock to…well, you name it and I probably have the album and can discuss it in contemporary context. I not only enjoy music, I have mastered the art of enjoying music. And, like any other passion, my enjoyment of music is enhanced by sharing it with others.

The Mix Tape, now the Mix CD, has been the vehicle by which I have shared my love of music. But, it’s a dangerous proposition. Just like John Cusack’s character in “High Fidelity”, you can easily craft a Mix that only serves to show off your musical taste and not evoke an emotional response from your recipient. The Mix should be treated like a gift. The purpose of a gift is to elicit an emotional response from the recipient, to evoke in the recipient the same feelings that led you to make the Mix in the first place. Look, you don’t by yourself a pair of shoes and tell your wife it’s a gift for her because you really like the shoes. The same goes with the Mix.

That being said, creating a good Mix is a very, very hard task. You must not only have an wikipedic knowledge of music, buy you must also know what the recipient enjoys and what songs will evoke the feelings you are seeking. Fortunately, I have all these things. And, thus, I make really good Mixes for my wife. She loves them and she’s not just saying that to save my feelings. I’ve let a few bad ones slip through Quality Control and I hear about it pretty quickly.

Up until a few weeks ago, the hardest part of the Mix was collecting the songs; in essence, asking the question “What goes with Hot Child in the City by Nick Gilder?” See, my wife loves this song. In fact, she tells me that this is her personal soundtrack that she hears in her head when she’s walking down the street. (Mine is Outa-Space by Billy Preston.)

What has now made this task much easier is the Genius feature in iTunes. I can simply select the song, hit the Genius button and get up to 100 songs in my library that match the song in question. In fact, it uses data from all iTunes users and the iTunes store to correlate songs together. So, if you hit refresh a few times, you get the idea that iTunes thinks “Kiss You Allover” and “Saturday Night” are good fits. That makes sense because “Kiss You All Over” was the song that “Hot Child in the City” knocked out of number one and “Saturday Night” was another late 70’s hit.

But hold on…what’s this?...”Voices Carry” by Til Tuesday!?!? First of all, I hate that song (I like Aimee Mann, though) . Second, I would never even think to put that on a CD. But, I think Maia likes it. So, it goes on the Mix.

So, what would normally take hours and hours of inefficient work, now becomes fun again. “Digging in the stacks” is no longer a reason to not make a Mix. From 100 songs, I can pare down what I think it appropriate and create a good 15 song Mix that really reflects that Maia’s taste and not mine. With some work from the computer, I can get some deeper tracks that I would have never thought off.

This is completely applicable to manually scripting your Java Application Server deployments. Whether you are using your own framework or someone else’s, you’re just working too hard. And for what purpose? Job security, the love of hacking scripts, inability to affect chance in your organization? Well, in fact, those are the most common reasons we have seen that individual contributors cite for using manual scripting.

Unfortunately, not a single one of those reasons will improve your company’s bottom line. Over the next few blog postings, I will revisit each of these ideas to show how all of these reasons are more than false; in fact, they’re dangerous to the individual contributor and the company.

In Untagged 
Comment (0) Read More...