FeaturesPluginsDocs & SupportCommunityPartners

>> More Visual Web Pack Documentation

Developing a Media Management Application

May 2007 [Revision number: V5.5.1-1]    

This article describes how to install and use the PhotoAlbum sample application, which is a web application through which users can manage different types of media--such as photos or audio data--stored in a local or remote database. The article highlights the important features of the PhotoAlbum sample application, and along the way illustrates how you might use the NetBeans IDE Visual Web Pack 5.5 module to implement a similar application.

Contents

About the PhotoAlbum Application
Getting Started With the PhotoAlbum Application
Making Effective Use of Components and Features
Coding Tips and Considerations
Summary
  Content on this page applies to NetBeans 5.5 and 5.5.1 Visual Web Pack

External code used in this tutorial

» PhotoAlbum.zip

This article works with the following technologies and resources

JavaServer Faces Components/
Java EE Platform
works with1.2 with Java EE 5*
works with1.1 with J2EE 1.4
Travel Database requiredNot required
BluePrints AJAX Component Library not requiredNot required

* As of the date this article was published, only the Sun Java System Application Server supported Java EE 5.

This article has been tailored for use with the Sun Java Application Server PE 9.0 Update Release 1 and with Tomcat 5.5.17. If you are using a different server, consult the Release Notes and FAQs for known problems and workarounds. For detailed information about the supported servers and Java EE platform, see the Release Notes.

About the PhotoAlbum Application

The PhotoAlbum application lets you create, modify, and delete media albums, such as photo albums. You can upload media from files into different media albums, review the uploaded media, and manage media album content. For example, if you want to organize a set of image or picture files, you can upload these pictures into different photo albums and view these pictures as thumbnails or singly in a full-scale preview mode. You can also manage albums and the pictures within individual albums.

Despite its name, the PhotoAlbum application is not limited to just image files; you can use it to store video and audio files within an album and preview them as well. Since the PhotoAlbum applications principal media types are images, this article refers to photos or image media throughout for illustration. However, keep in mind that the same principles apply to audio and video media types. (Please note that the application does not reliably handle video media types at this time, due to the size of video files.)

This article is divided into the following sections:

Getting Started With the PhotoAlbum Application

This section describes how to install and run the application.

Installing the PhotoAlbum Application


Download the zip file for the PhotoAlbum application.

Set Up the Server and Database


Start the Sun Java System Application Server 9, if not already running. From the Runtime pane, right click the Sun Java System Application Server 9 node and select Start. A green arrow to the left of the server name indicates that the server is running.

The PhotoAlbum application requires that you set up a database (data source) that includes several tables. You do this from within the IDE after opening the PhotoAlbum project. Here are the steps to do this. Note, too, that the application was developed with Java Studio Creator and will be converted to a NetBeans application when you open it.
  1. Open the project. From the File -> Open Project dialog, navigate to the PhotoAlbum project directory. Select the project directory and click Open Project Folder.
  2. Create the database for the project. From the Tools menu, select the Java DB Database->Create Java DB Database... option. In the Create Java DB Database dialog, set all the fields--the database name, user name, and password--to mediaalbum.
  3. Set up the JDBC connection to the database. In the Runtime pane, expand the Databases node and right click the mediaalbum (jdbc:derby://localhost:1527/mediaalbum [mediaalbum on MEDIAALBUM]) node, then click the Connect option.
  4. Open and execute the SQL script supplied with the PhotoAlbum project to create the tables in the mediaalbum database.

    1. From the Files pane, expand the PhotoAlbum node and open the folder called dbscripts.
    2. Double-click the create_media_db.sql file to open it. The file contents display in the editing window.
    3. Be sure that the Connection box indicates the mediaalbum database (jdbc:derby://localhost:1527:mediaalbum [mediaalbum on MEDIAALBUM]). Note that you might need to widen the Edit window to see the mediaalbum database in the Connection box.
    4. Click the Run SQL icon to execute the SQL statements in the script file. (See Figure 1.) If you have not already connected to the mediaalbum database, a dialog opens displaying the username, mediaalbum, and prompts you for the password. Enter the password: mediaalbum.
    5. The Output window at the bottom displays the commands as they execute and the PhotoAlbum tables are created.

      Figure 1:  Execute SQL to Create Database Tables
      Figure 1: Execute SQL to Create Database Tables (click to see full-sized image)

  5. In the Runtime pane, you should be able to expand the mediaalbum node and see its tables and columns.

  6. You may need to resolve the database or datasource reference. From the Projects pane, expand the PhotoAlbum->Data Source References node. If the Media Album node is marked with a red badge, you need to resolve the data source reference to the database. Right click Data Source References and click the Resolve Data Source(s)... option. In the dialog that opens, set the Database URL to jdbc:derby://localhost:1527/mediaalbum and both the user name and pasword to mediaalbum. (For more information on resolving data sources, you may want to refer to the article "Importing a Sun Java Studio Creator 2 Project in NetBeans Visual Web Pack 5.5".)

You only need to do the above steps once. Once setup is complete, you should be able to connect to the jdbc:derby://localhost:1527/mediaalbum [mediaalbum on MEDIAALBUM] database by just entering the username/password combination of mediaalbum/mediaalbum.

Using the PhotoAlbum Application


Run the PhotoAlbum application from the IDE (right click the project name and select Run Project). The application's main page lets you manage your albums--you can choose to create multiple albums at one time or return to this page later and create (or delete) additional albums.

Start by creating one or more albums. Give each album you create a name. (Notice that the Name field is preceded by an asterisk, indicating that the field is required.) Optionally, you may provide a description for an album. Click Create Album to create the album. See Figure 2.

Figure 2:  PhotoAlbum Manager Main Screen
Figure 2: PhotoAlbum Manager Main Screen (click to see full-sized image)

After creating your album(s), you can open individual albums and upload pictures to the opened album. Click the Open Album button next to the album you want to open. (Click the Delete Album button to delete an album.)

You can perform the following operations on an opened album (see Figure 3):

  • Upload media files--Click the Upload Media button to browse to a folder with the media files you want to upload. You can select files that are located on the same system or choose files across the network. It is important to note that this application works most reliably with simple JPEG and small .wav files; other file formats may cause errors.

    Note: There are some limitations on the types and sizes of media files that you can upload to an album. For image files, its best if you use only JPEG, GIF, and PNG files. You should also ensure that the file names do not contain embedded spaces. File names should not exceed 15 characters in length. Currently, the application does not reliably handle video file types because of their size and you should avoid loading these types of files.

    Note: To properly load large audio files, please refer to the NetBeans tip for modifying the maximum file upload size at http://www.netbeans.org/kb/55/vwp-fileupload.html#06.
  • Delete previously loaded media files--Click the Delete button next to a media file to delete that one file.
  • Display full-sized image files--There are several ways to display the image files in full size:

    • Click the Display button next to an image file to open a full-size display of that image. You may have to scroll down to see the image display, since it appears at the bottom of the screen. Figure 3 illustrates using the Display button.
    • Or, simply click the image preview thumbnail to display the full-sized image in the entire browser window. (Use the Back arrow to return to the application.)
    • If you prefer, you can display the full-sized image in a separate browser window. To do so, right click the image preview thumbnail and choose the option Open Link in New Window.

      Clicking the image preview and the Display button work when the media type is image, audio, or video.
  • Move one or more selected files to another album--Select one or more media files by checking the box in the right-most column for a file. Move all selected files to the album displayed in the Moved Selected to pull-down list.
  • Delete multiple media files--Similar to moving files between albums, you can also select multiple files for deletion and delete them in one operation. Select the files to delete by checking the box in that media files right-most column. Then, click the Delete Selected button.
  • Return to the album management screen--Click the To albums list button to return to the screen for managing albums.

    Figure 3: Managing Photos Within an Album
    Figure 3: Managing Photos Within an Album (click to see full-sized image)

Making Effective Use of Components and Features

This section shows you how to effectively use some key Visual Web Pack components and features. It covers the following:

Designing a Web Page


The PhotoAlbum uses the Visual Web Pack layout components to control the placement and alignment of components on the page. These layout components, particularly Grid Panel, Layout Panel, and Page Fragment, make it easy to design your web page. Let's take a closer look at how to get the most from these components.

Page Fragment

Use page fragments when you want to set up a consistent look to multiple pages of a web application. Page fragments are particularly useful for establishing uniform web page headers, footers, and side bars. You design the page fragment once, then place it where appropriate on different web pages.

For example, you might want a consistent masthead or banner across all application pages. You define this look in a single page fragment, then include that page fragment on the application pages. For a banner, you might place the masthead page fragment at the top of each web page.

We created the Masthead Page Fragment (in the Projects pane, right-click the Web Pages node within the project, then select New -> Page Fragment...) and designed the PhotoAlbum Manager banner shown in Figure 4.

Figure 4: Masthead Page Fragment
Figure 4: Masthead Page Fragment (click to see full-sized image)

Later, as we worked on the applications pages, such as AlbumList, we simply dropped a Page Fragment Box from the Palette onto the page, positioned it where we wanted the banner to appear (at the top of the page), and selected the MastheadFragment Page Fragment from the Select Page Fragment dialog drop down list. (See Figure 5.) Page Fragments can be copied and pasted among projects, which is an even simpler way to place a Page Fragment on a page.

Figure 5: Adding a Page Fragment Box to a Page
Figure 5: Adding a Page Fragment Box to a Page (click to see full-sized image)

Grid Panel Component

A Grid Panel component is useful for arranging text and other components on a page. When you drop a Grid Panel on a page, it creates a table to which you then add other components. The added components display starting from left to right and from top to bottom. (By toggling the Grid Panel components dir property you can set the direction of display to either left to right or right to left.) By default, a Grid Panel has one column and as many rows as needed to accommodate components dropped on it.

You can change the number of columns for a Grid Panel after it is added to the page--you merely change the columns value in the Grid Panels Properties sheet. Grid Panel components can also be nested within other Grid Panel components, which gives you even greater control for positioning components on a page. You can resize a Grid Panel directly on the page. Or, for maximum control, open the Style Editor from the Grid Panel components style property (click ... next to style). You can use this editor to set background color, margins, size, position, and so forth.

For example, the MastheadFragment Page Fragment makes good use of Grid Panel components. Notice that the banner includes a bottom panel that displays two links, one to Creator Home and the other to a Help file. We wanted these links to be on the right side of the page and beneath the PhotoAlbum Manager banner, and using several Grid Panel components gave us the control we wanted. (Note: The PhotoAlbum application was actually developed for the Java Studio Creator IDE and that is why it contains a link to the Creator home page. It is a simple matter to change this display and the associated link, as you will soon see.)

First, we dropped a Grid Panel on the page, which we called bottomPanel, then resized its width to match that of the top banner and its height to a size that would accommodate the images and text. We also set its columns property to two and set a background color. Next, we dropped two Grid Panel components on top of bottomPanel. When you drop a component onto another component, be sure that the component already on the page is selected--highlighted in blue--before you execute the drop. Since we set bottomPanel to two columns and the display direction is the default left to right, these two Grid Panel components display within bottomPanel from left to right. Also, the background color we set for bottomPanel carries through as the background color for the components dropped onto it. (Notice in Figure 6 that the leftPanel is highlighted to help you locate it on the page; normally, its background color is the same as the bottomPanel and rightPanel.)

To keep things clear, we labelled the Grid Panel added first as leftPanel, since it appears in the left column, and the other as rightPanel. We intended the leftPanel component to serve as a space holder, so we just set its width to cover the left half of the page. Since the rightPanel holds the two links, which we want to display side-by-side, we also changed the rightPanel columns value to two. (See Figure 6.)

Figure 6: Using Grid Panel Components
Figure 6: Using Grid Panel Components (click to see full-sized image)

Next, we dropped two Image Hyperlink components onto the rightPanel. We set the text property for the leftmost Image Hyperlink, called creatorHome, to the text that should appear on the page, in this case "Creator Home", and its imageURL property to an image file, the home.gif file in the resources folder. (Both these properties are in the Properties Appearance section.) The textPosition property, set to right, ensures that text is positioned to the right of the image associated with the hyperlink. We also set its url property (found in the Properties Behavior section) to the URL for Java Studio Creator, http://developers.sun.com/prodtech/javatools/jscreator/index.jsp, so that when users click this link they are taken to the IDEs home page. (See Figure 7.)

Figure 7: Adding Hyperlinks to a Grid Panel
Figure 7: Adding Hyperlinks to a Grid Panel (click to see full-sized image)

We set up the rightmost Image Hyperlink component, which we called help, similarly: Its text property is "Help", its imageURL is a help.gif file in the /resources folder, and its url points to a Readme.html file. If a user clicks on this hyperlink, the Readme help text displays.

Layout Panel Component

A Layout Panel component gives you another option for arranging components on a page. The AlbumView page makes use of several Layout Panel components. A Layout Panel functions much like a Grid Panel--place a Layout Panel on a page and then drop components onto the Layout Panel-- but a Layout Panel gives you more flexibility in arranging components within the panel. Specifically, it lets you arrange added components either in a flow or grid layout. What does this mean?

When you use a Layout Panel whose mode is set to Flow Layout, the IDE places components dropped on the panel starting in the top left corner. Components are added to the panel from left to right across the top row of the panel, until that row is filled. Subsequent components are added from left to right in the next row down, and so forth. You can drop a new component to the left of a component already placed in the panel by hovering over the previously added component until a vertical mark appears on the left of that component.

Grid Layout mode for a Layout Panel works only if you have set the IDE to use the snap to grid option. Otherwise, even if Grid Layout is set for the panel, it functions just like Flow Layout. (Note: You can set up snap to grid from the Tools -> Options-> Visual Designer settings and at the same time customize the grid pattern size.) When the Layout Panel is set to Grid Layout, added components appear in the panel aligned to the grid location at which they were added. If you use the snap to grid feature--by clicking the Align context-sensitive menu option for the entire Layout Panel or for a component within a Layout Panel--then components in the panel are positioned relative to the nearest grid corner.

A Layout Panel with all its contained components also always appears on its own line when the page is rendered at runtime. That is, it appears separated from any components above and below it. Use the Group Panel component if you want the ability to place a panel on the same line as other components.

Controlling User Input Processing With Virtual Forms


The application makes good use of virtual forms, a feature of the IDE that lets you limit the portions of user input that are processed when a web page is submitted. By designating some user input within a virtual form, that portion of input can be processed separately from input elsewhere on the page.

How Virtual Forms Are Used

Let's take a look at how two pages, AlbumView and AlbumList, use virtual forms. The AlbumView page defines two virtual forms--deleteSelected and selectAll--and several components on the page are included in these virtual forms. Figure 8 shows the AlbumView page with virtual forms displayed.

Note: Although AlbumView displays two virtual forms, the application actually uses only the deleteSelected virtual form. While the selectAll virtual form is currently not necessary nor used, it would be useful should we decide to add a Select All get action event handler.

Figure 8: AlbumView Virtual Forms
Figure 8: AlbumView Virtual Forms (click to see full-sized image)

Notice that the components for selecting, moving, and deleting files from an album (Select All/Move Selected to/Delete Selected buttons, album pull-down list, and individual file checkboxes) are outlined in green or blue. Components outlined in green are part of the selectAll virtual form, while those outlined in blue are part of the deleteSelected virtual form. Furthermore, a solid outline indicates an input component that participates in the virtual form. A broken or dotted outline indicates a submission component, which is a component that submits the virtual form for processing.

Configuring Virtual Forms

Use a component's context-sensitive Configure Virtual Forms... function to add that component to virtual forms. (See Figure 9.) The Configure Virtual Forms dialog shows that the selected component--in this example, btnMoveSelected--is a submission component for the deleteSelected virtual form. You can change whether btnMoveSelected participates or submits for any existing virtual form on the page by double-clicking in the appropriate row and column. The Participate and Submit column entries display a Yes/No pull-down list if the selected component is of the right type. Click the New button to create a new virtual form, for which you can then configure the selected component.

Figure 9: Configuring Virtual Form
Figure 9: Configuring Virtual Form (click to see full-sized image)

When the web page user interacts with a virtual forms submission component, such as by clicking the Move Selected to button, processing affects only the virtual forms input or participant components and ignores other input components on the page. By using virtual forms, the PhotoAlbum application confines certain operations to selected files or database table rows. For example, if the user selects several media files (by checking the checkbox for those files) from an albums list of files, then clicks the Move Selected to button with a target photo album designated from the pull-down list, subsequent processing moves only the selected files from the current album to the designated album.

The AlbumList page also defines two virtual forms, which are noted simply as virtualForm1 and virtualForm2. These virtual forms keep the delete and open album operations separate from the create album operations. (Notice that virtualForm1 limits processing to the Open Album buttons and virtualForm2 limits processing to the Delete Album buttons.) That is, the virtual forms ensure that input in the panel for creating an album is ignored if the user clicks either the Open Album or Delete Album button. (See Figure 10.)

Figure 10: Virtual Forms in AlbumList Page
Figure 10: Virtual Forms in AlbumList Page (click to see full-sized image)

Considerations When Using Virtual Forms

There are some considerations to keep in mind when using virtual forms.

For one, if you use page fragments, note that there is no support for virtual forms within page fragments.

Also, virtual forms and a components immediate property, along with the Auto-submit on Change setting, have some overlapping capabilities. When the value changes for a component that has set the immediate property, the new value is immediately converted and validated in the JavaServer Faces Apply Request Values phase rather than in the subsequent Process Validations phase.You place code for handling the conversion and validation return values, such as an appropriate error message, in the components processValueChange action handler.

Notice that the Drop Down component for View Style drop down list on the AlbumView page has its immediate property checked to indicate it is set. The View Style component also has Auto-submit on Change set, which automatically submits the form when the component value changes. Thus, when the user selects a new value from the drop down list, the server performs any conversion and validation and then executes the value change listener methods for the component, and then the page is redisplayed. If you want code to execute after the validation and processing, you need to place it in the components processValueChange method. In this example, the viewStyle_processValueChange method includes code to display an error message.

If you have a component configured so that Auto-submit on Change is set, you can also use virtual forms to limit the input fields that are processed when the form is submitted. You can define the auto-submit component to submit a virtual form, in which case you are assured that only the virtual form participants are processed when the auto-submit occurs.

Although virtual forms are not supported by page fragments, virtual forms can still work around some problems that can occur with page fragments. One such problem has to do with triggering actions. Notice that the MastheadFragment includes links that ordinarily would trigger a page submit and subsequent UploadFile actions.

Coding Tips and Considerations

This section provides some "behind the scenes" coding tips.

Writing SQL Scripts and Using Predefined SQL Scripts


Notice that the PhotoAlbum project included a predefined SQL script, create-media-db.sql, which contained the SQL commands necessary to create and define the PhotoAlbum database tables. You configured the database by opening this predefined script in the IDE and executing the SQL statements.

Recall that you can execute all the SQL statements displayed in a window, such as all the statements in the script file, by clicking the Run SQL icon (see Figure 11). You can also right click within the window itself and select the Run Statement option to execute an individual SQL statement.

Figure 11: Run SQL Icon
Figure 11: Run SQL Icon

You can use this same approach to make other SQL script files available to the IDE. For example, write your own SQL commands using any text editor and place them in one or more text files. Then, include these files with your project simply by placing the text files with the SQL statements somewhere beneath your projects top directory. The IDE can access just about any file that is in a directory or folder beneath the project directory. For example, the PhotoAlbum top directory is within a ...\Projects directory. The PhotoAlbum directory has a number of subdirectories, such as \src and \build . For the PhotoAlbum project, we created an additional subdirectory for SQL scripts, called it \dbscripts , and placed the text file containing the SQL statements within that directory. The Files pane shows all the subdirectories (or folders) and files beneath the top project directory and provides easy access to them.

Another way to execute SQL statements is to open a Query window: Right click a database table node in the Runtime pane, then select View Data. The Query window displays the SQL statement for that table, which you can replace with the SQL statements from the script file and execute by clicking the Run SQL icon. You can also write and execute SQL statements directly in the Query window, by replacing the default query with your own SQL statements.

Executing SQL Commands


The PhotoAlbum application includes a method, executeSQLUpdate, that can be used to execute any SQL INSERT, UPDATE, or DELETE command. Three page beans in the application--AlbumView, AlbumList, and PictureView--call this method when they need to execute SQL commands. They first construct a string with the SQL operation, such as a delete or update operation, and then pass that string to the executeSQLUpdate method to execute. Because its operation is intended to span the entire application, the executeSQLUpdate method is contained within the ApplicationBean1 source.

For example, the AlbumView page bean includes an action handler to delete a media entry from the database. Using the identifier (or rowkey) referencing the database row to delete, the AlbumView delete_action method constructs the SQL DELETE command and calls getApplicationBean1().executeSQLUpdate to execute the command.

Code Sample 1: delete_action method
public String delete_action() {
    RowKey rowKey = tableRowGroup1.getRowKey().getApplicationBean1().executeSQLUpdate
       "DELETE FROM MEDIA WHERE MEDIAID="+mediaDataProvider.getValue
	       ("MEDIAID", rowKey));
    return null;
}

The same AlbumView page bean has another method, btnMoveSelected_action, that moves selected images from one album to another; that is, it moves selected rows from one database table to another. The btnMoveSelected_action method similarly builds a String object with the row identifiers of the selected rows and invokes executeSQLUpdate to execute the UPDATE command:

Code Sample 2: UPDATE command
getApplicationBean1().executeSQLUpdate(
 	"UPDATE MEDIA SET ALBUMID="+albumList.getValue()+
 	" WHERE MEDIAID IN ("+ids.toString().substring(1)+")");
	

Here is the source for the executeSQLUpdate method defined in ApplicationBean1. You can use this same method to execute SQL commands on other database tables merely by substituting your datasource name for Media Album in the ctx.lookup statement.

Code Sample 3: executeSQLUpdate Method
public void executeSQLUpdate(String query){
      Connection conn = null;
      Statement sqlStatement = null;
    try {
      		javax.naming.Context ctx = new javax.naming.InitialContext();
      		DataSource ds = (DataSource)ctx.lookup(
 	  "java:comp/env/jdbc/Media Album") ;
      	conn = ds.getConnection()
      	// setup the connection
      	conn.setAutoCommit(false);
      	// execute the query
      	sqlStatement = conn.createStatement();
      	sqlStatement.executeUpdate(query);
      	conn.commit();
      } catch (Exception ex) {
      error("Exception during update: " + ex.getMessage() );
      	try {
      		if ( conn != null ) {
      			conn.rollback() ;
      		}
      	} catch (SQLException sqle) {
      log("Error on rollback " + sqle.getMessage() );
      	}
      } finally {
      	// close the statement
      	if ( sqlStatement != null ) {
      		try {
      			sqlStatement.close() ;
      		} catch (Exception ex) {
      log("Error Description", ex);
         		}
          }
      	if ( conn != null ) {
      		// cleanup and close the connection.
      		try {
      			conn.close() ;
      		} catch (Exception ex) {
                 log("Error Closing connection ", ex);
      		}
      	}
      }
    }

You can also execute SQL statements from within a servlet, which the application does when it retrieves the BLOB data from the database. See Using a Servlet to Retrieve BLOB Data From a Database for details.

Uploading BLOB Data to a Database


The PhotoAlbum application demonstrates how to upload media files to a database in the form of BLOB data and then retrieve the same BLOB data from the database. BLOB (or binary large object) data is a collection of binary data that is stored as a single entity in a database. A DBMS that accepts BLOB data usually permits a BLOB column in a database to be any length. Generally, since BLOB data is in binary format and thus is extremely flexible, you use BLOBs to hold multimedia objects (images, videos, and sound). You can also use them to store program code.

This section shows how to upload BLOB data from a media file to a database table, while the next section describes how to retrieve BLOB data from a table to use in an application.

The general steps to upload a media file, such as an image file, are as follows:

  • Get the image file data as byte[] data.
  • Append a row to the data provider associated with the database table into which the data will be uploaded.
  • Set the value of the BLOB column in the data provider to the byte[] data.
  • Insert the appended row into the table by invoking the data provider commitChanges method.

The UploadFile page bean contains the Java code for uploading a media file to the database. The code of interest is part of the upload_action method. Double click the Upload button at the bottom of the UploadFile page to go right to this method. (See Figure 12.)

Figure 12: Run SQL Icon
Figure 12: Upload Button In UploadFile Page (click image to enlarge)

Determining File Content Type

Let's look at what the upload_action method does to handle files containing image data. The method first uses the fileUpload.getUploadFile().getContentType method to determine the type of content in the file selected for uploading. (The getContentType method is part of the com.sun.rave.web.ui.component package. You can view the Javadoc for this package, along with the method and related classes and interfaces, by selecting the Help -> Javadoc References -> Sun Web UI Component References option.)

Code Sample 4: getContentType Method
...
String contentType=fileUpload.getUploadedFile().getContentType();
int mediaType=getApplicationBean1().MEDIA_TYPE_UNKNOWN;
if (contentType.substring(0, 5).compareTo("image") == 0){
   mediaType=getApplicationBean1().MEDIA_TYPE_IMAGE;
}
...

We use the getContentType method to extract the selected files content type to contentType, and then test a substring of contentType for a match on "image". The application defines a constant (MEDIA_TYPE_IMAGE) in ApplicationBean1, which it assigns a value of 0, and uses this constant to identify image data files. The method retrieves the constant from ApplicationBean1 using getApplicationBean1().MEDIA_TYPE_IMAGE and, if the contentType substring matches "image", it sets the variable mediaType to 0.

Appending a Row to a Data Provider

Next, to add the image data to the database, we prepare to append a row to the dataprovider, mediaCreateDataProvider. The IDE created the mediaCreateDataProvider when the MEDIA data table was added to the page when the page was designed. At that time, it added the following code to the UploadFile page bean:

Code Sample 5: Appending a Row to a Data Provider
private CachedRowSetDataProvider mediaCreateDataProvider
    = new CachedRowSetDataProvider();
public CachedRowSetDataProvider getMediaCreateDataProvider() {
   return mediaCreateDataProvider;
}
public void setMediaCreateDataProvider(CachedRowSetDataProvider crsdp) {
   this.mediaCreateDataProvider = crsdp;
}

To append a row, we use methods from the CachedRowSetDataProvider class. We call the appendRow method, which returns the row key of the next available row in the data table, and then call setCursorRow to set the dataproviders cursor to that row key, as follows:

Code Sample 6: appendRow method
...
RowKey rk = mediaCreateDataProvider.appendRow();
mediaCreateDataProvider.setCursorRow(rk);
...

Once the set up is done, we then set the values for the columns (ALBUMID, NAME, MEDIA, SIZE, and so forth) in the MEDIA table. We use the CachedRowSetDataProvider setValue method to do so. Notice that the MEDIA column holds the BLOB image data itself. We set the data into this column using the fileUpload.getUploadedFile().getBytes() method, which returns the contents of the selected file as byte data.

Code Sample 7: CachedRowSetDataProvider setValue method
...
mediaCreateDataProvider.setValue("MEDIA.ALBUMID",
      getSessionBean1().getCurrentAlbumId());
mediaCreateDataProvider.setValue(
      "MEDIA.MEDIA",fileUpload.getUploadedFile().getBytes());
   mediaCreateDataProvider.setValue("MEDIA.NAME", name.getValue());
...

The PREVIEW column holds a preview version of the image data, and we set that column, too, using the same getBytes method. See Creating a Preview of an Image for more information on sizing a preview image.

Code Sample 8: PREVIEW Column
...
byte[] preview=null;
switch (mediaType){
   case 0://getApplicationBean1().MEDIA_TYPE_IMAGE:
        preview=getPreview(fileUpload.getUploadedFile().getBytes(),
                fileUpload.getUploadedFile().getContentType().substring(6));
                break;
   ...
   mediaCreateDataProvider.setValue("MEDIA.MEDIA_PREVIEW",preview);
...

Once we've set all the data table column values, we can save the new row to the table by invoking the commitChanges method. We invoke the refresh method to ensure that everything is up to date.

Code Sample 9: commitChanges method
...
mediaCreateDataProvider.commitChanges();
mediaCreateDataProvider.refresh();
...

Here's the upload_action method in its entirety:

Code Sample 10: upload_action method
public String upload_action() {
   String contentType=fileUpload.getUploadedFile().getContentType();
   //detect media type
   int mediaType=getApplicationBean1().MEDIA_TYPE_UNKNOWN;
   if (contentType.substring(0, 5).compareTo("image") == 0){
      mediaType=getApplicationBean1().MEDIA_TYPE_IMAGE;
   }
   if (contentType.substring(0, 5).compareTo("video") == 0){
      mediaType=getApplicationBean1().MEDIA_TYPE_VIDEO;
   }
   if (contentType.substring(0, 5).compareTo("audio") == 0){
      mediaType=getApplicationBean1().MEDIA_TYPE_AUDIO;
   }
   //fill data provider fields
   try {
      if ( mediaType!=getApplicationBean1().MEDIA_TYPE_UNKNOWN) {
       RowKey rk = mediaCreateDataProvider.appendRow();
       mediaCreateDataProvider.setCursorRow(rk);
       mediaCreateDataProvider.setValue("MEDIA.ALBUMID",
getSessionBean1().getCurrentAlbumId()); mediaCreateDataProvider.setValue("MEDIA.MEDIA", fileUpload.getUploadedFile().getBytes()); mediaCreateDataProvider.setValue("MEDIA.NAME", name.getValue()); mediaCreateDataProvider.setValue("MEDIA.DESCRIPTION", description.getValue()); mediaCreateDataProvider.setValue("MEDIA.MEDIA_TYPE", new Integer(mediaType)); mediaCreateDataProvider.setValue("MEDIA.CONTENT_TYPE",contentType); mediaCreateDataProvider.setValue("MEDIA.SIZE",
new BigDecimal(fileUpload.getUploadedFile().getSize())); byte[] preview=null; switch (mediaType){ case 0://getApplicationBean1().MEDIA_TYPE_IMAGE: preview=getPreview(fileUpload.getUploadedFile().getBytes(),
fileUpload.getUploadedFile().getContentType().substring(6)) break; case 1://getApplicationBean1().MEDIA_TYPE_VIDEO: preview=getVideoPreview(); break; case 2://getApplicationBean1().MEDIA_TYPE_AUDIO: preview=getAudioPreview(); break; } mediaCreateDataProvider.setValue("MEDIA.MEDIA_PREVIEW",preview); mediaCreateDataProvider.commitChanges(); mediaCreateDataProvider.refresh(); } else { error("Please choose a file of image, audio or video type.
"+fileUpload.getUploadedFile().getOriginalName()+" is of
"+fileUpload.getUploadedFile().getContentType()+" type."); return null; } } catch (Exception ex) { mediaCreateDataProvider.revertChanges(); log("Error Description: ", ex); error("Error comes: "+ex.getMessage()); return null; } return "returnAfterUpload" ; }

Using a Servlet to Retrieve BLOB Data From a Database


The application retrieves BLOB data from the database using a servlet, DisplayImage.java, that was written for this purpose. It demonstrates how to get the image data and use a ResultSet to return the data for display. You can find the source code for DisplayImage.java from within the Files pane for the project. (See Figure 13.)

Figure 13: DisplayImage.java Servlet
Figure 13: DisplayImage.java Servlet

While you can write the Java code for a servlet in a separate editor and then add it to the project, you can also create the class from within the IDE. From the same Files pane, right click the project node, then select New -> Java Class. Follow the prompts in the New Java Class wizard to create the new Java class.

Figure 14: Creating a New Java Class
Figure 14: Creating a New Java Class

The two methods of interest in the DisplayImage servlet are:

  • processRequest --Does the set up work to prepare for the BLOB data retrieval
  • getImage --Retrieves the BLOB data

Let's look at the processRequest method first:

Code Sample 11: processRequest method
protected void processRequest(HttpServletRequest request,
     HttpServletResponse response) throws ServletException, IOException {
String id=request.getParameter("imageid"); String ct=request.getParameter("contenttype"); boolean preview=request.getParameter("preview")!=null; if ((ct==null)||(ct.equals(""))) { ct="image/bmp"; } try { ServletOutputStream out = response.getOutputStream(); response.setContentType(ct); out.write(this.getImage(id,preview)); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } }

The processRequest method, which the doGet and doPost methods invoke, does some set up work in preparation for retrieving the BLOB data. It gets the media identifier, its content type, and sets a boolean value based on the preview parameter. The code ensures that the content type, if null or empty, is set to a default of image/bmp , and then calls the getImage method, which actually does the data retrieval. If data is returned, then the processRequest method sets the returned results for output and the BLOB data eventually displays:

Code Sample 12: Preparation for Retrieving BLOB Data
   ...
   ServletOutputStream out = response.getOutputStream();
   response.setContentType(ct);
   out.write(this.getImage(id,preview));
   ...
   	

Now let's look at the code for the getImage method:

Code Sample 13: getImage method
private byte[] getImage(String id, boolean preview) throws IOException {
   Statement sta=null;
   Connection conn=null;
   ResultSet rs=null;
   byte[] result=null;
   try {
      if (id==null) return null;
      Context initContext = new InitialContext();
      DataSource ds = (DataSource)initContext.lookup("jdbc/Media Album");
      conn = ds.getConnection();
      sta = conn.createStatement();
      String mediaColumn=preview?"MEDIA_PREVIEW":"MEDIA";
      rs=sta.executeQuery("SELECT " + mediaColumn +
          " FROM MEDIAALBUM.MEDIA where MEDIAID="+id);
      if (rs.next()) {
          result=rs.getBytes(mediaColumn);
      } else {
          System.out.println(
          "Could find image with the ID specified or there is a problem
           with the database connection");
      }
      rs.close();
   } catch (Exception e) {
      System.out.println(e.getMessage());
      e.printStackTrace();
   } finally {
      try{
         sta.close();
         conn.close();
      } catch (Exception ex) {
         log("Error Closing connection ", ex);
      }
      return result;
   }
}
	

The getImage method does the initial look up of the Media Album table and establishes a JDBC connection to the table. It then creates, builds, and executes an SQL query, selecting either the data table MEDIA_PREVIEW column or the MEDIA column data, depending on the boolean preview:

Code Sample 14: Establish JDBC Connection and Execute SQL Query
...
sta = conn.createStatement();
String mediaColumn=preview?"MEDIA_PREVIEW":"MEDIA";
rs=sta.executeQuery("SELECT " + mediaColumn +
   " FROM MEDIAALBUM.MEDIA where MEDIAID="+id);
...
	

If the query is successful, the method reads the bytes stored in the MEDIA_PREVIEW or MEDIA column into an array in the ResultSet. It then closes the query and returns the results to the processRequest method, which we've already seen handles preparing the data for display:

Code Sample 15: Action if Query is Successful
if (rs.next()) {
   result=rs.getBytes(mediaColumn);
 else { ... }
 ...
 rs.close();
 ...
 return result;
}
	

Creating a Preview of an Image


As we indicated in the section Uploading BLOB Data to a Database, the MEDIA table holds BLOB data in two of its columns, MEDIA and MEDIA_PREVIEW, and both of these columns are filled during the media upload operation.

When image data is uploaded, the MEDIA_PREVIEW column holds a scaled-down or reduced version of the BLOB data that is suitable for previewing. In the case of video or audio data, the application uses one of two fixed or static icons. You can view these two icons, audio_preview.gif and video_preview.JPG, by double-clicking on them in the projects Web Pages -> resources folder. (See Figure 15.)

Figure 15: Audio and Video Preview Icons Figure 15: Audio and Video Preview Icons
Figure 15: Audio and Video Preview Icons

The code for creating a media preview is in the UploadFile page bean. The upload_action method determines and then invokes the appropriate method for uploading preview binary data using the following switch statement:

Code Sample 16: UploadFile Page Bean
...
switch (mediaType){
   case 0://getApplicationBean1().MEDIA_TYPE_IMAGE:
      preview=getPreview(fileUpload.getUploadedFile().getBytes(),
         fileUpload.getUploadedFile().getContentType().substring(6));
      break;
   case 1://getApplicationBean1().MEDIA_TYPE_VIDEO:
      preview=getVideoPreview();
      break;
   case 2://getApplicationBean1().MEDIA_TYPE_AUDIO:
      preview=getAudioPreview();
      break;
}
	

The getVideoPreview and getAudioPreview methods merely read in the bytes of the respective resource image files. The getPreview method, on the other hand, scales the actual image file to a suitable size. It first determines if the image size is such that scaling is even necessary, since small images may already fit in the preview display. If scaling is needed, the key call is to the scale method on the Graphics2D class, after which getPreview returns the scaled image as a byte array:

Code Sample 17: Scaling an Image
...
BufferedImage newImage = new BufferedImage(newWidth, newHeight,
   BufferedImage.TYPE_INT_RGB);
Graphics2D g2d=( Graphics2D )newImage.getGraphics();
g2d.scale(((double)newWidth)/image.getWidth(),
   ((double)newHeight)/image.getHeight());
g2d.drawImage(image,0,0,new JFrame());
ImageIO.write(newImage, format, result);
...
return result.toByteArray();

Invoking JavaScript


NetBeans Visual Web Pack makes it easy to invoke JavaScript from within an application. As you know, JavaScript code can be embedded within the JavaServer Pages code, identified by its own set of tags.

The IDE lets you bind JavaScript code to a component event so that the code is invoked when the event occurs. You do the binding through the JavaScript section of a components Properties window. Select the component and locate the event in the component's Properties window JavaScript section. Enter the JavaScript code directly in the Properties window, or click the "..." box to open an editor window.

To illustrate, suppose you want the user to confirm an action connected to a button click. Select the button component, and then, in the JavaScript section of the components Properties window, click the "..." box of the target event, which in this case is onClick. This opens an editor window into which you might enter the following JavaScript code:

 return confirm ("Are you sure?")

When you are finished, the component's Properties sheet looks as shown in Figure 16.

Figure 16: JavaScript Section of Properties Window
Figure 16: JavaScript Section of Properties Window

The PhotoAlbum application uses JavaScript code to obtain confirmation from a user for various operations. The application binds JavaScript code to the delete buttons (Delete, Delete Selected, and Delete Album) to compel the user to respond to a confirmation message before the deletion occurs. JavaScript is also used for actions that change component state (the Select All and Unselect All buttons), for validation checks (the Move Selected to button), and for setting a file name when uploading media data. Figure 17 shows the JavaScript associated with the Move Selected to buttons validation checks. The complete JavaScript code was added using the Properties editor window. Notice that you can insert code that is fairly complex.

Figure 17: JavaScript for Move Selected Button
Figure 17: JavaScript for Move Selected Button (click image to enlarge)

Note, too, that the IDE inserts the JavaScript you enter through the editor into the JavaServer Pages code for the component. For example, the JavaScript code for the MoveSelected button's onClick property appears as follows in the AlbumView JSP:

Code Sample 18: AlbumView JSP
<ui:button action="#{AlbumView.btnMoveSelected_action}"
   binding="#{AlbumView.btnMoveSelected}" id="btnMoveSelected"
onClick="var i;&#xa;for (i=0;i&lt;document.forms[0].elements.length;i++)
   {&#xa;  if (document.forms[0].elements[i].checked){&#xa;
   return true;  &#xa;  }&#xa;}&#xa;alert(&quot;
   No items selected&quot;);&#xa;return false;" text="Move Selected to"/>
   	

When you use the Properties editor window to enter JavaScript code, the IDE handles inserting the JavaScript into the JSP page with the proper tags and formatting. Of course, you can always edit the JSP yourself and manually add the JavaScript.

The IDE provides other ways to use JavaScript in an application. You can put the JavaScript code in a separate file and then use the Script component to bind to that file. First, enter the code to a JavaScript file, giving the file a .js extension, and then add the file to the project. (Place the file in a subdirectory of the Project directory.) Next, drop a Script component, found in the Advanced section of the Palette, onto a page and bind the Script components url property to the .js file.

You can also drop the Script component onto the page and add your JavaScript code directly in the JSP editor. When added to a page, a Script component does not display but the IDE adds the line

<ui:script binding="#{AlbumView.script1}" id="script1"/>

to the JSP page. You can then insert your JavaScript code within that tag, being sure to include the ending </ui:script> tag. For example, you might have:

<ui:script binding="#{AlbumView.script1}" id="script1"
   return confirm("Are you sure?")
</ui:script>

Using the Script component is more complicated, but it does allow you to add generic JavaScript code that is not bound to an event.

Summary

The PhotoAlbum application provides a good starting point for developing your own NetBeans Visual Web Pack media management or database-oriented application. It shows you how to work with media files and use the data provider interface to load and retrieve files containing large amounts of binary data to and from database tables.

The application covers tips for using SQL and servlets from within the IDE and your application, along with including JavaScript code in an application. It also covers effectively using some of the IDE components to organize the presentation of your web pages.


This page was last modified:  May 24, 2007


Bookmark this page

del.icio.us furl simpy slashdot technorati digg
Companion
Projects:
MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Virtual Box - full virtualizer  Open ESB - The Open Enterprise Service Bus Powered by