This chapter will introduce the development of applications using J2EE technologies, such as Java, Java Server Pages (JSP), and Java Server Faces (JSF), as well as Oracle’s Application Development Framework (ADF).
Oracle’s Application Development Framework (ADF) is a collection of technologies that can be used to automate the complexities of developing an application using J2EE’s model-view-controller (MVC) design paradigm. This allows the developer to concentrate on the business logic. ADF is based on proven J2EE technologies, such as Java Server Faces (JSF), and business components from previous versions of Oracle JDeveloper.
Development of a rich-media application using Java and J2EE technologies is much quicker and easier with the use of an integrated development environment (IDE) such as JDeveloper. This is especially true when the IDE includes wizards to access media data. However, some may choose, for various reasons, not to use an IDE for development. For this reason, this chapter will show how an IDE can be used for interMedia data as well as plain code samples. The reason for this is that the concepts presented here can be used for multiple J2EE IDEs as well as for those developing J2EE applications without the use of an IDE.
Some may choose to use plain Java applications to access media, while others will integrate this media access into JSP pages. The choice of using plain Java or JSP technology is typically one made outside of the question of where the media is going to be managed. The concepts in this chapter are how to use these J2EE technologies to use media professionally managed in the database using these technologies.
To make J2EE development simpler and more accessible, Oracle JDeveloper introduces the Oracle Application Development Framework (Oracle ADF), a J2EE development framework based on the model-view-controller (MVC) architecture that implements design patterns and eliminates infrastructure coding. Oracle ADF has four layers:
The business services layer—. provides access to data from various sources and handles business logic.
The model layer—. provides an abstraction layer on top of the business services layer, enabling the view and controller layers to work with different implementations of business services in a consistent way.
The controller layer—. provides a mechanism to control the flow of the Web application.
The view layer—. provides the user interface to the application.
Oracle ADF integrates seamlessly with interMedia at all layers, thus providing great flexibility and simplicity in the way in which developers can create media-rich Java applications.
On the business services layer, developers can drag and drop tables from the database browser onto the UML diagram to generate business services that provide Java interfaces to these tables. interMedia object types are automatically recognized by Oracle ADF and corresponding domain classes are created on this layer. The same mechanism also makes the interMedia object transparent in the drag-and-drop operations on the model layer and the controller layer.
On the view layer, developers can create a media-rich JSP/UIX application as any other JSP/UIX application by the drag-and-drop business services component from JDeveloper Data Control Palette. Render value tag and input render tag will automatically bind user interface components with interMedia domain objects so that users are able to view, insert, update, and delete multimedia content just like handling other textual data in the JSP/UIX application. The developers can debug or run the JSP/UIX application from inside the JDeveloper. When the development work is done, the developers can deploy the JSP/UIX application to an application server in the form of WAR file or EAR file from the JDeveloper.
Finally, developers can choose to program directly against the underlying interMedia ADF Integration Package. The interMedia ADF Integration Package includes the interMedia ADF domain classes and a set of utility classes. The OrdImageDomain, OrdAudioDomain, OrdVideoDomain
, and OrdDocDomain
interMedia ADF domain classes are available since Release 9i of JDeveloper. These domain classes are wrappers of the interMedia Java Client classes described earlier and inherit all the underlying multimedia retrieval, upload, and manipulation methods.
The interMedia business components domain classes support the DomainOwnerInterface, LobInterface, AttributeList
, and XMLDomainInterface
of the Oracle ADF, and so provide built-in, integrated multimedia capabilities. The utility classes support the retrieval, rendering, and uploading of multimedia content. For example, any application can use the interMedia ADF domain classes to facilitate uploading multimedia into the database. Servlet and JSP applications can use the OrdURLBuilder
and OrdPlayMedia
classes to build URLs and retrieve multimedia content from the database. OrdURLBuilder
constructs URLs that locate interMedia objects using the ADF run-time framework, while OrdPlayMedia
interprets the URLs to fetch the interMedia content from the database and deliver it to the browser.
To illustrate this point, we will take the steps necessary to create a very simple media application using JDeveloper ADF.
To create a model component to interface to the database and retrieve and upload media from the photos table that was created in previous samples, business components are used.
To create the model, we must first create an application and database connection. This is accomplished using simple wizards. The Database Connection connects to a database where the photos table was created.
After creating a JDeveloper application and database connection, we create a project to hold the model and use a JDeveloper wizard to create the model. In the General category, Projects or ADF Business Components is selected. We choose Business Components to create a component that can retrieve/insert data from/to a database table. See Figure 9.1.
After naming the model project “MediaProject” we accept the defaults in the next screen that specifies the paths. After the second step in creating the project, the following screen shown in Figure 9.2 is displayed to guide us through creating the business object from database tables. In this screen we choose the database connection we had defined previously.
After defining the connection to use, it is time to select the table to build the business component to use. JDeveloper displays a list of database tables when the Tables object type checkbox is selected. The photos table is selected by identifying the object type as a table and selecting the Photos table, as shown in Figure 9.3
The next step is to create an Updatable View Object (Figure 9.4). This updatable view business object allows for a view that can be updated by the application. The following step would be to create read-only view objects that we will skip.
After skipping the read-only object view selection, we take the defaults on the next screen that define the package and application module name. On the last screen of this wizard, we can optionally create a business components diagram of this module. This can be useful to understand the configuration of the business object. We choose to create a diagram.
At this point, we have created a business object that can be used by a model component.
To create a JSF application, we use the JDeveloper. First an empty project is created and then we create a new JSF page control and configuration (faces configuration) into the project. This controller will handle the control component of the JSF application. After these steps, JDeveloper has the appearance shown in Figure 9.5.
After creation of the faces configuration file, named faces-config.xml by default, JDeveloper immediately brings the faces configuration file to the foreground and displays it in a Design GUI. To see the actual XML source code, you can press the source tab at the lower portion of the center screen. At this point, there is no actual content in the JSF application, so the Design GUI is empty.
On the upper right side there is a Palette that contains components. Using the mouse, drag a JSF page component from the Palette onto the Design screen. At this point the Design screen contains a page that will be invoked by the JSF controller. We double click on the page to bring up the Create JSF JSP wizard. On the second screen of the wizard the newly created JSP is named browse.jsp, as shown in Figure 9.6.
After this page of the wizard, we take the defaults for the next screen and then continue to choose the tag libraries we will use. We choose the tag libraries illustrated in Figure 9.7.
Then, click Finish to take the rest of the defaults for this new JSP page. In a real application, we would at least review and verify the default choices, and perhaps change them to what the development environment requires.
After creation of the JSF page, we place it within a JSP Editor. From our Palette, we choose data controls, expand AppModuleDataControl, and locate the PhotosView1 component. This component is dragged onto the JSP screen. A dialog appears, and we choose Create->Table->ADF Read-Only Form. A dialog appears to let us choose which columns will appear on the screen. For this example, the only image we want to display is the thumbnail image in the table, since it will display a list. We could, and should, show the full size image in a real application by creating a link on this page to a detail page. We will display all the text fields, as well as the thumbnail image.
We delete the Image column, and are left with the rest of the columns. We also want some navigation and a submit button, so we check these items. Figure 9.8 shows the state before we continue.
At this point, the design screen has the appearance shown in Figure 9.9.
We will be concerned with the row called Thumb. This row needs to be rendered as an image rather than text. Before we can render this as an image, we will need to configure the page to be able to render the thumbnail as an image. This involves the following steps:
For the first set, we use the navigator on the left side to expand the application sources in MyJMFProject. In the list is a package called myjmfproject.pageDefs. This project includes definitions for each of the JSF pages in the project. After expanding the myjmfproject.pageDefs project and opening browsePageDef.xml, we can see the definitions for each of the fields. The definition that will be modified is highlighted. See Figure 9.10.
The highlighted definition for Thumb will be a custom input handler set to OrdDomainValueHandler. After this change, the XML segment describing Thumb will be the following:
<attributeValues id="Thumb" IterBinding="PhotosView1Iterator"
CustomInputHandler="OrdDomainValueHandler">
<AttrNames>
<Item Value="Thumb"/>
</AttrNames>
</attributeValues>
At this point, we need to create a servlet endpoint for media requests. The OrdDomainHandler uses the OrdDeliverMedia servlet. Creation of this endpoint is done by modifying web.xml. To open web.xml, use the navigator to expand Web Content and then WEB-INF. Open web.xml. The Following XML segment is added to the XML file to define the servlet that will be used for getting (or putting) media from (or to) the database. This XML segment is placed within the upper-level <web-app> tag. This change needs to only be done once in the project, not like the change of the InputHandler that needs to be done for every page that will want to display media from the database or insert media into the database.
<filter-mapping> <filter-name>adfBindings</filter-name> <servlet-name>ordDeliverMedia</servlet-name> </filter-mapping> <servlet> <servlet-name>ordDeliverMedia</servlet-name> <servlet-class>oracle.ord.html.OrdPlayMediaServlet</ servlet-class> <init-param> <param-name>releaseMode</param-name> <param-value>Stateful</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ordDeliverMedia</servlet-name> <url-pattern>ordDeliverMedia</url-pattern> </servlet-mapping>
At this point, we have to go into the source code of the JSP. Select the tab for browse.jsp. Choose the Source tab at the lower part of the main screen. Find the following code in browse.jsp:
<af:outputText value="#{bindings.Thumb.inputValue}"/>
Change this text to output media with the MIME type of the media with the following objectMedia tag:
<af:objectMedia source="#{bindings.Thumb.inputValue.source}" contentType="#{bindings.Thumb.inputValue.media.mimeType}"/>
The ObjectMedia tag has a source, which indicates the source of the data with a URL, and a contentType, that is the MIME type of the media. If the media is an image, an image tag is produced, otherwise an anchor tag is produced (for nonimage media, you should add something within the ObjectMedia tag to click on).
We are nearly there! Now, only one thing is left to be done before we can see the image. We need to add the jar file BC4JHTML.jar to the project. To do this, highlight the MyJMFProject in the navigator, right click on it, and choose Project Properties. Now highlight Libraries and click on the Add Library button. Select BC4JHTML and click OK. Click OK to leave the project properties dialog. We are ready to test the JSP page.
To test browse.jsp, select it in the navigator, right click, and select run. If all went well, you should see something like the screen in Figure 9.11.
To create a form to edit or create media in the database is quite similar. The differences are:
The form must be changed to be of type multipart/form data. This can be done in many ways, like double clicking on the <h:form> element on the lower left. The <h:form> element should end up looking like:
<h:form enctype=îmultipart/form-data
The form field to edit/create the media field must be changed to accept a file. The wizard will put in code that looks like the following:
<af:inputText value="#{bindings.Image.inputValue}" label="#{bindings.Image.label}" required="#{bindings.Image.mandatory}" columns="#{bindings.Image.displayWidth}">
<af:validator binding="#{bindings.Image.validator}"/> </af:inputText>
This should be changed to something like the following that will create a field to accept a filename for update/insert and also display the media when updating an existing record:
<af:panelLabelAndMessage> <af:inputFile value="#{bindings.Image.inputValue}" simple="true"> <af:validator binding="#{bindings.Image.validator}"/> </af:inputFile> <af:objectMedia source="#{bindings.Image.inputValue.source}" contentType="#{bindings.Image.inputValue.media.mimeType}"/> </af:panelLabelAndMessage> >
Oracle interMedia provides a custom Java Server Pages (JSP) tag library that lets users easily generate multimedia HTML tags in Java Server Pages, and upload multimedia data into interMedia objects in the database.
The interMedia JSP tag library is used with Oracle JDeveloper, however, the application can be deployed on the J2EE platform of your choice.
Oracle interMedia Java Classes for servlets and JSP uses the OrdHttpResponseHandler class to retrieve media data from an Oracle database and deliver it to a browser or other HTTP client from a Java servlet or JSP page.
Multimedia Tag Library provides media retrieval tags, which JSP developers can use to generate complete HTML multimedia tags or create multimedia retrieval URLs for inclusion in the customized use of an HTML multimedia tag. The media retrieval tags are embedAudio, embedImage, embedVideo, and mediaURL.
The PhotoAlbum.jsp
file is one component of a sample JSP application that uses tags from the Multimedia Tag Library to retrieve media data from the database and deliver it to a browser, which displays the media in a simple photograph album application. Example 2-1 shows the tags mediaUrl and embedImage.
The PhotoAlbum.jsp
file generates the HTML code that displays the contents of the database table named Photos, including the contents of the description, location, and thumb columns. The contents of the thumb column in the photos table are displayed as thumbnail images that link to the full-size images that are stored in the image column in the Photos Table. From the browser, users can click a thumbnail image to view the full-size image.
[1] <%@ page language="java" %> [2] <%@ taglib prefix="ord" uri="/Web-inf/intermedia taglib.tld" %> <%@ taglib prefix="sql" uri="/web-inf/sqltaglib.tld" <% [3] public static final String escapeHtmlString(String input) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < input.length(); i++) { char ch = input.charAt(i); switch (ch) { case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '&': sb.append("&"); break; case '"': sb.append("""); break; case ' ': sb.append(" "); break; default: sb.append(ch); } } return sb.toString(); } %> <%-- HTML header --%> <HTML LANG="EN"> <HEAD> <TITLE>interMedia JavaServer Pages Photo Album Demo</TITLE> </HEAD> <BODY> <%-- Page heading --%> [4] <TABLE BORDER="0" WIDTH="100%"> <TR> <TD COLSPAN="2" BGCOLOR="#F7F7E7" ALIGN="CENTER"> <FONT SIZE="+2"> <I>inter</I>Media JavaServer Pages Photo Album Demo </FONT> </TD> </TR> </TABLE> <P> <TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%" SUMMARY="Table of thumb nail images"< <TR BGCOLOR="#336699"> <TH id="description"><FONT COLOR="#FFFFFF">Description</FONT></TH> <TH id="location"><FONT COLOR="#FFFFFF">Location </FONT></TH> <TH id="image"><FONT COLOR="#FFFFFF">Image</FONT></TH> </TR> <% int rowCount = 0; %> [5] <sql:dbOpen connId = "myConn" dataSource="jdbc/OracleDS" /> <sql:dbQuery connId = "myConn" queryId="myQuery" output="jdbc"> SELECT id, description, location from photos order by description </sql:dbQuery> <sql:dbNextRow queryId="myQuery"> <% [6] String id = myQuery.getString(1); String description = myQuery.getString(2); String location = myQuery.getString(3); %> [7] <TR> <TD HEADERS="description"> <%= escapeHtmlString(description) %> </TD> <% if ( location != null ) out.print( "<TD HEADERS="location">" + escapeHtmlString(location) + "</TD>" ); else out.print( "<TD HEADERS="location"> </TD>" ); %> <TD HEADERS="image"> [8] <ord:mediaUrl dataSourcename="jdbc/OracleDS" table = "photos" column = "image" key = "<%=id%>" keyColumn = "id" id = "urlId"> [9] <A HREF="<%= urlId.getUrl()%>"> [10] <ord:embedImage dataSourceName="jdbc/OracleDS" table = "photos" column = "thumb" key = "<%= id %>" keyColumn = "id" alt = "<%=escapeHtmlString(description)%>" border="1" /> </A> </ord:mediaUrl> </TD></TR> [11] <% rowCount ++; %> </sql:dbNextRow> <sql:dbCloseQuery queryId="myQuery"/> <sql:dbClose connId="myConn"/> <TR> <TD SCOPE="col" COLSPAN="3" ALIGN="CENTER"> <FONT COLOR="#336699"><B><I> <% if (rowCount == 0) { out.println(" The photo album is empty"); } else { out.println (" Select the thumb-nail to view the full-size image"); } %> </I></B></FONT></TD> </TR> <%-- Finish the table --%> </TABLE> </P> <P> <TABLE WIDTH="100%"> <TR BGCOLOR="#F7F7E7"> <TD COLSPAN="3" ALIGN="CENTER"> <A HREF="PhotoAlbumUploadForm.jsp">Upload new photo</A> </TD> </TR> </TABLE> </P> </BODY> </HTML>
The Java, SQL, and HTML statements in the PhotoAlbum.jsp
file perform the following operations:
Declare Java as the script language used in the JSP page. (This line of code is a JSP directive.)
Provide the prefix and uri attributes. The value of the uri attribute indicates the location of the tag library descriptor (TLD) file for the tag library. The prefix attribute (ord) specifies the XML namespace identifier, which should be inserted before each occurrence of the library’s tags in the JSP page. (These two lines of code are Multimedia Tag Library directives.)
Declare a method that provides escape sequences to interpret some commonly used special characters in HTML. (This is called a method declaration statement.)
Use an HTML table to display the entries of the photos table in the database. (This is an HTML program.)
Open the database connection, perform a query on the photos table, and then loop over the retrieved result set.
Retrieve data from the result set.
Begin to display the entries in the table.
Create a script variable named urlId that points to the image column of the photos table in the database. (This line of code shows the Multimedia JSP tag mediaUrl.)
Provide a link that points to the URL stored in the script variable.
Generate the HTML <IMG> tag that displays the thumb column of the photos table (see embedImage for information about the HTML output). The HTML <A HREF> tag uses the JSP tag embedImage as the link anchor. (This line of code shows the Multimedia JSP tag embedImage.)
End the loop then close the query and the database connection.
Oracle interMedia Java Classes for servlets and JSP uses the OrdHttpUploadFile class to facilitate the handling of uploaded media files. This class provides a simple application programming interface (API) that applications call to load media data into the database.
File uploading using HTML forms encodes form data and uploaded files in POST requests using the multipart/form-data format. The OrdHttpUploadFormData class facilitates the processing of such requests by parsing the POST data and making the contents of regular form fields and the contents of uploaded files readily accessible to a Java servlet or JSP page.
Multimedia Tag Library provides media upload tags, which facilitate the development of multimedia applications that upload media data into the database. The media upload tags are storeMedia, uploadFile, and uploadFormData.
The PhotoAlbumInsertPhoto.jsp
file is one component of a sample JSP application that uses tags from Multimedia Tag Library to upload media files into a database. The following example shows the tags uploadFormData, uploadFile, and storeMedia.
[1] <%@ page language="java" %> <%@ taglib prefix="ord" uri="/Web-inf/intermedia-taglib.tld" %> <%@ taglib prefix="sql" uri="/web-inf/sqltaglib.tld" %> [2] <ord:uploadFormData formDataId = "fd"> [3] <ord:uploadFile parameter = "photo" fullFileName = "ffName" shortFileName = "sfName" length = "fLength" > <% [4] if (ffName == null || ffName.length() == 0) { %> <jsp:forward page="PhotoAlbumUploadForm.jsp?error= Please+supply+a+file+name."/> <% return; } if (fLength.intValue() == 0) { %> <jsp:forward page="PhotoAlbumUploadForm.jsp?error= Please+supply+a+valid+image+file."/> <% return; } String description = fd.getParameter("description"); String location = fd.getParameter("location"); [5] if ( description == null || description.length() == 0 ) { description = "Image from file: " + sfName + "."; if(description.length() > 40) { description = description.substring(0, 40); } } java.util.Vector otherValuesVector = new java.util.Vector(); otherValuesVector.add(description); otherValuesVector.add(location); %> <%String id = "original"; %> [6] <sql:dbOpen connId = "myConn" dataSource="jdbc/OracleDS" commitOnClose="true"/> <sql:dbQuery connId = "myConn" queryId= "myQuery" output="jdbc"> SELECT photos_sequence.nextval from dual </sql:dbQuery> <sql:dbNextRow queryId="myQuery"> <% id = myQuery.getString(1); %> </sql:dbNextRow> <sql:dbCloseQuery queryId="myQuery"/> [7] <ord:storeMedia conn = "<%= (oracle.jdbc.driver.OracleConnection)myConn.getConnection() %>" table = "photos" key = "<%=id%>" keyColumn = "id" mediaColumns = "image" mediaParameters = "photo" otherColumns = "description, location" otherValues = "<%=otherValuesVector%>" /> [8] <sql:dbSetParam name = "myid" value = "<%=id%>"/> <sql:dbExecute connId = "myConn" bindParams="myid"> {call generateThumbNail(?)} </sql:dbExecute> <sql:dbClose connId = "myConn" /> </ord:uploadFile> </ord:uploadFormData> <%-- HTML header --%> <HTML LANG="EN"> <HEAD> <TITLE>interMedia JavaServer Pages Photo Album Demo</TITLE> </HEAD> [9] <META HTTP-EQUIV="REFRESH" CONTENT="2;URL=PhotoAlbum.jsp"> <BODY> <%-- Page heading --%> <TABLE BORDER="0" WIDTH="100%"> <TR> <TD COLSPAN="2" BGCOLOR="#F7F7E7" ALIGN="CENTER"> <FONT SIZE="+2"> <I>inter</I>Media JavaServer Pages Photo Album Demo </FONT> </TD> </TR> </TABLE> <%-- Display header and instructions --%> <P> <FONT SIZE=3 COLOR="#336699"> <B>Photo successfully uploaded into photo album</B> </FONT> <HR SIZE=1> </P> <P> Please click the link below or wait for the browser to refresh the page. </P> <%-- Output link to return to the main page --%> <P> <TABLE WIDTH="100%"> <TR BGCOLOR="#F7F7E7"> <TD COLSPAN="3" ALIGN="CENTER"> <A HREF="PhotoAlbum.jsp">Return to photo album</A> </TD> </TR> </TABLE> </P> <%-- Finish the page --%> </BODY> </HTML>
The Java, SQL, and HTML statements in the PhotoAlbumInsert-Photo.jsp
file perform the following operations:
Declare Java as the script language used in the JSP page (JSP directive). Provide the location of the TLD file and the required ord and sql prefix attributes. (These are Tag Library directives.)
Create a script variable named fd, which is an instance of the oracle.ord.im.OrdHttpUploadFormData object. (This line of code shows the multimedia JSP tag uploadFormData.)
Create the script variables ffName, sfName, and fLength, which contain the full file name, short file name, and file length of the uploaded media, respectively. (This line of code shows the multimedia JSP tag uploadFile.)
Provide error checking.
Generate a default description if no description is provided.
Open the database connection and get the next unique ID for the photos table in the database.
Upload the media data into the image column of the photos table, and the description and location information into the description and location columns of the photos table. (This line of code shows the multimedia JSP tag storeMedia.)
Call a PL/SQL procedure to populate the thumb column of the photos table from the uploaded image column.
Display a message of success and then direct the JSP page back to the main page (PhotoAlbum.jsp
).
The image proxy classes were introduced in chapter 2. Aside from the OrdImage class, the other classes are OrdAudio, OrdVideo, and OrdDoc. These classes make the functionality of the interMedia classes available to Java programs outside the database. They also include utility classes to make programming common functions more convenient.
The database table used for proxy classes can be created from the Java program or previously using standard SQL. In this chapter, it is assumed that the table is created outside the Java program. Most client programs should not be performing database definition language (DDL) statements that, in most cases, are left to the DBA.
You will note that many of the methods have a context parameter. You may wonder why it is there and why you should care. In most cases you should not, however, it is not used by most interMedia users.
Where it is used is if you have a user-written source plug-in. This may be something like a legacy laser disk picture server that can only be accessed using an API. If you do have a system like this, you may need to store a context when the system is opened. Perhaps device handles, current position, etc.
So, if you write a user-written source plug-in, you do care about the context parameter and you will store your context there. If you are using the Database BLOB source, the HTTP source, or file source, you should not worry about the context parameter, except to know you need to pass it in many of the APIs.
The proxy classes must be associated with a media object in the database. They are used to represent the database media objects in the Java program. The creation of the media object can be done from Java with a simple insert statement, after the object is created, and it can be associated with a proxy class and its contents manipulated. To create the association between the proxy class and the database media object, JDBC is used. To use the proxy classes, the following actions may be taken:
Create a JDBC connection, with AutoCommit set to off unless no SQL update operations are to occur, then the programmer can choose either AutoCommit on or off.
A new media row may be created.
Obtain the media object into a CallableStatement or ResultSet from a procedure or a select statement.
Use the proxy classes.
Update the proxy classes.
The creation of a JDBC connection is the same as you would do for using an SQL connection on any database, except it is typically important to set AutoCommit to off for operations that will update the database. Note that AutoCommit is set on by default in JDBC connections. For example:
// register the oracle jdbc driver with the JDBC driver manager DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver()); Connection conn = DriverManager.getConnection(connectString, username, password); // Note: for update operations, it is CRITICAL to set the autocommit to false so that // two-phase select-commit of BLOBS can occur. conn.setAutoCommit(false);
To create a new media row, a simple insert statement is executed with the media column inserted. Typically, when you insert a new media object, you are going to put media data into it in subsequent operations, so to avoid round trips, you may want to return the initialized media object. To be a bit more efficient, we will insert and return the image object in one step using a JDBC CallableStatement block.
// After prepare, this can be used over and over again. CallableStatement cstmt = conn.prepareCall ( "begin " + "insert into photos t (id, description, location," + image, thumb) " + " values (?," + "'Desc that should be an input param''," "'A place that should be an input param too', " + "ORDImage.init(), ORDImage.init())" + " returning rowid, t.image, t.thumb into ?, ?, ? ; " + "end;"); // Register input parameters cstmt.setInt(1, 17); // Register Output Parameters cstmt.registerOutParameter(2, Types.VARCHAR); // rowid ((OracleCallableStatement)cstmt).registerOutParameter (3, Types.STRUCT, "ORDIMAGE"); // image ((OracleCallableStatement)cstmt).registerOutParameter (4, Types.STRUCT, "ORDIMAGE"); // thumb int rowsUpdated = cstmt.executeUpdate(); String rowid = cstmt.getString(2); // Obtain the proxy objects OrdImage imgObj = (OrdImage)((OracleCallableStatement)cstmt).getORAData (3, OrdImage.getORADataFactory()); OrdImage thumbObj = (OrdImage)((OracleCallableStatement)cstmt).getORAData (4, OrdImage.getORADataFactory());
To return the media rows from an existing or just inserted row, we could also use a standard select statement.
// select the new ORDImage into a java proxy OrdImage object (imageProxy) String rowSelectSQL = "select image from photos where id = 1 for update"; OracleResultSet rset = (OracleResultSet)stmt.executeQuery(rowSelectSQL); rset.next(); OrdImage imageProxy = (OrdImage)rset.getORAData("image", OrdImage.getORADataFactory()); rset.close();
Note that the for update
clause should only be used if there is a potential of the media, or row, being updated. The media is typically updated through the proxy object.
Once obtained, the proxy classes can be used. They can be used to retrieve media or attributes, or they can be used to modify the media and attributes. They are like any other Java object except that the modifications can be made permanent in the database.
One example of using a proxy class would be to create an image using Image IO. This image then can be used in other display objects, like an ImageIcon that can be put into various controls, like a JLabel that can be displayed in a frame.
InputStream is = imgObj.getContent().getBinaryStream(); ImageInputStream iis = ImageIO.createImageInputStream(is); Image image = ImageIO.read(iis); ImageIcon imgIcon = new ImageIcon(image); JLabel label = new Jlabel(imgIcon);
Another example is to use the methods provided to perform image processing and modify images in the database. For example:
imgObj.loadDataFromFile("goats.gif"); imgObj.setProperties(); imgObj.processCopy("fileFormat=JFIF maxScale=128 128", thumbObj);
If you change the Java proxy object during processing, these changes do not become permanent until the row is updated and the current transaction committed. Here is an example of updating and committing the update after proxy object changes.
OraclePreparedStatement insertImg = (OraclePreparedStatement)conn.prepareStatement( "Update photos Set image = ?, thumb = ? " + " where rowid = ?" ); insertImg.setORAData(1, imgObj); insertImg.setORAData(2, thumbObj); insertImg.setString(3, rowid); insertImg.execute(); insertImg.close(); conn.commit();
Common methods for OrdAudio, OrdImage, OrdVideo, and OrdDoc
Table 9.1 contains a signature and brief description of the common methods. For full information, see the interMedia Java Classes Reference Manual.
Table 9.1. Common Methods
Method Signature | Description |
---|---|
| Clears the local attribute to indicate that the media data is stored externally. |
| Closes a plug-in data source. |
| Deletes media stored locally in the source BLOB. |
| Exports data from the source BLOB into a file. |
| Returns a BFILE locator from the database when the media is stored in a file outside the database. |
| Returns the BLOB locator from the localData attribute. |
| Returns the length of the media data. |
| Returns a byte array containing the media data from the localData BLOB attribute. |
| Writes the data from the database specified by the localData BLOB attribute to a local file. |
Returns an InputStream object from which the data in the database BLOB specified by the localData attribute. | |
| Returns the OrdAudio ORADataFactory interface for use by the getORAData() method. |
| This method returns the value of the format attribute as a string. |
| This method returns the value of the mimeType attribute as a string. |
| Returns the source information in the form srcType://srcLocation/srcName. |
| Returns the value of the srcLocation attribute. |
| This method returns the value of the src-Name attribute. |
| This method returns the value of the srcType attribute. |
| This method returns the value of the updateTime attribute. |
| This method returns true if the data is stored in the database in a BLOB; false otherwise. |
| Loads data from a byte array into the database BLOB specified by localData; it replaces any existing content and updates the Update Time. |
| Loads data from a file local to the Java program into the database BLOB specified by localData; it replaces any existing content and updates the Update Time. |
| Loads data from an InputStream into the database BLOB specified by localData; it replaces any existing content and updates the Update Time. |
| Sets the value of the format attribute. |
| Sets the value of the local attribute to indicate that the media data is stored locally in the database in a BLOB specified by the localData attribute. |
| Sets the value of the mimeType attribute. |
| Sets the values of the srcType, srcLocation, and srcName attributes. |
| Sets the value of the updateTime attribute. |
Table 9.2 shows the OrdImage noncommon methods.
Table 9.2. OrdImage Noncommon Methods
Method Signature | Description |
---|---|
| Checks if the properties of the image data are consistent with the attributes of the OrdImage Java object. |
| Copies all the attributes of the current OrdImage. If the media is stored locally in a BLOB, the BLOB is also copied. |
| Returns the value of the compressionFormat attribute. |
| Returns the value of the contentFormat attribute. |
| This method returns the value of the height attribute. |
| This method returns the value of the width attribute. |
Imports data from an external source into the database BLOB specified by the localData attribute. Calls setProperties() after obtaining the data unless the setFormat() method sets the format attribute. | |
| Imports data from an external source, specified by the method parameters, into the database BLOB specified by the localData attribute. Calls setProperties() after obtaining the data unless the setFormat() method sets the format attribute. |
| Performs one or more image-processing operations on the image data in the database BLOB specified by the localData attribute. |
| Copies the image data to the destination object and performs one or more imageprocessing operations on the image data. |
| Sets the value of the compressionFormat attribute. |
| Sets the value of the contentFormat attribute. |
| Sets the value of the contentLength attribute, but does not affect the media itself. |
| Sets the value of the height attribute. |
| Parses the image data properties and sets the values of the attributes in the OrdImage Java object. |
| Writes the characteristics of a foreign image into the appropriate attribute fields. See the interMedia Reference Guide for the format of this string. |
| Sets the value of the width attribute. |
Table 9.3 shows the OrdAudio noncommon methods.
Table 9.3. OrdAudio Noncommon Methods
Method Signature | Description |
---|---|
| Checks if the properties of the audio data are consistent with the attributes of the OrdAudio object. |
| Returns the values of the audio properties in a temporary CLOB in a form defined by the format plug-in. |
| Returns the value of the contentFormat attribute. |
| Returns the value of the requested audio property defined by user-defined format plug-ins. |
| This method returns the value of the audioDuration attribute. |
| Returns the CLOB locator from the comments attribute. |
| Returns the value of the compressionType attribute. |
| Returns the data from the BLOB specified by the localData attribute in a temporary BLOB in the database. |
| Returns the length of the audio data. |
| Returns the length of the audio data using source plug-in context information. Not supported for all source types. |
| Returns the value of the description attribute. |
| This method returns the value of the encoding attribute. |
| Returns the value of the numberOfChannels attribute. |
Returns the value of the samplingRate attribute. | |
| Imports data from an external source into the database BLOB specified by the localData attribute. |
| Imports data from an external source, specified by the method parameters, into the database BLOB, specified by the localData attribute. Calls setProperties() after obtaining the data unless the setFormat() method sets the format attribute. |
| Opens a data source for a plug-in if necessary. |
| Calls the format plug-in in the database to execute a command implemented by a user-written plug-in. |
| Calls the user-written source plug-in in the database to execute a command. |
| Reads data from the data source. |
| Sets the value of the audioDuration attribute. |
| Sets the value of the comments attribute. |
| Sets the value of the compressionType attribute. Set automatically for some formats with setProperties(Byte[][]). |
| Sets the value of the description attribute. |
| Sets the value of the encoding attribute. May be set with setProperties(Byte[][]). |
| Sets the values of the known attributes of the OrdAudio Java object. SetProperties(Byte[][]) may set these as well. |
| Sets the value of the numberOfChannels attribute. |
| Parses the audio data properties and sets the values of the attributes in the OrdAudio Java object. |
| Parses the audio data properties, sets the values of the attributes in the OrdAudio Java object, and optionally populates the CLOB specified by the comments attribute. |
| Sets the value of the sampleSize attribute. |
| Sets the value of the samplingRate attribute. |
| Trims the data to the specified length by source plug-ins that support the operation. |
| Writes data to the data source for source plug-ins that support it. |
Table 9.4 shows the OrdVideo noncommon methods.
Table 9.4. OrdVideo Noncommon Methods
Description | |
---|---|
| Checks if the properties of the audio data are consistent with the attributes of the OrdAudio object. |
| Returns the values of the audio properties in a temporary CLOB in a form defined by the format plug-in. |
| Returns the value of the contentFormat attribute. |
| Returns the value of the requested audio property defined by user-defined format plug-ins. |
| Returns the value of the bitRate attribute. |
| Returns the CLOB locator from the comments attribute. |
| Returns the value of the compressionType attribute. |
| Returns the data from the BLOB specified by the localData attribute in a temporary BLOB in the database. |
| Returns the length of the audio data. |
| Returns the length of the audio data using source plug-in context information. Not supported for all source types. |
| Returns the value of the description attribute. |
| This method returns the value of the encoding attribute. |
| Returns the value of the frameRate attribute. |
| Returns the value of the frameResolution attribute. |
| Returns the value of the height attribute. |
Returns the value of the numberOfColors attribute. | |
| Returns the value of the numberOfFrames attribute. |
| Returns the value of the videoDuration attribute. |
| Returns the value of the width attribute. |
| Imports data from an external source into the database BLOB specified by the localData attribute. |
| Imports data from an external source, specified by the method parameters, into the database BLOB, specified by the localData attribute. Calls setProperties() after obtaining the data unless the setFormat() method sets the format attribute. |
| Opens a data source for a plug-in if necessary. |
| Calls the user-written source plug-in in the database to execute a command. |
| Calls the format plug-in in the database to execute a command implemented by a user-written plug-in. |
| Reads data from the data source. |
| Sets the value of the bitRate attribute. |
| Sets the value of the comments attribute. |
| Sets the value of the compressionType attribute. Set automatically for some formats with setProperties(Byte[][]). |
Sets the value of the description attribute. | |
| Sets the value of the encoding attribute. May be set with setProperties(Byte[][]). |
| Sets the value of the frameRate attribute. |
| Sets the value of the frameResolution attribute. |
| Sets the value of the height attribute. |
| Sets the value of the numberOfColors attribute. |
| Sets the value of the numberOfFrames attribute. |
| Parses the audio data properties and sets the values of the attributes in the OrdAudio Java object. |
| Parses the audio data properties, sets the values of the attributes in the OrdAudio Java object, and optionally populates the CLOB specified by the comments attribute. |
| Sets the value of the videoDuration attribute. |
| Sets the value of the width attribute. |
| Trims the data to the specified length by source plug-ins that support the operation. |
| Writes data to the data source for source plug-ins that support it. |
Table 9.5 shows the OrdDoc noncommon methods.
Table 9.5. OrdVideo Noncommon Methods
Method Signature | Description |
---|---|
Returns the CLOB locator from the comments attribute. | |
| Returns the data from the BLOB specified by the localData attribute in a temporary BLOB in the database. |
| Returns the length of the audio data. |
| Imports data from an external source into the database BLOB specified by the localData attribute. |
| Imports data from an external source, specified by the method parameters, into the database BLOB, specified by the localData attribute. Calls setProperties() after obtaining the data unless the setFormat() method sets the format attribute. |
| Opens a data source for a plug-in if necessary. |
| Calls the user-written source plug-in in the database to execute a command. |
| Reads data from the data source. |
| Sets the value of the comments attribute. |
| Parses the audio data properties and sets the values of the attributes in the OrdAudio Java object. |
| Parses the audio data properties, sets the values of the attributes in the OrdAudio Java object, and optionally populates the CLOB specified by the comments attribute. |
| Trims the data to the specified length by source plug-ins that support the operation. |
int writeToSource(byte[ ] [ ] ctx, int startpos, int numbytes, byte[ ] buffer) | Writes data to the data source for source plug-ins that support it. |
OrdImageSignature is used as a proxy to the database OrdImageSignature database type. This signature describes the contents of the image in terms of color, shape, and texture. The image signature is used for image-matching applications. This is a quick list of the methods available.
To use these methods, the OrdImageSignature object must be populated and associated with a signature object in the back end. See Table 9.6 and the following code, for example.
Table 9.6. OrdImageSignature Methods
Method Signature | Description |
---|---|
| Compares two image signatures, returning a score that indicates the degree of difference between the image signatures. |
| Reads data from the data source. Generates an image signature for the specified image. The signature is stored in the OrdImageSignature object. Must be updated and committed into the database to be stored. |
| Returns the OrdImageSignature ORADataFactory interface for use by the getORAData() method. |
| Compares two image signatures, returning a status that indicates if the degree of difference between the image signatures is within a specified threshold. |
/ select the ORDImageSignature into a java proxy OrdImageSignature object (imageSigProxy) String rowSelectSQL = "select imageSig from photos"; OracleResultSet rset = (OracleResultSet)stmt.executeQuery(rowSelectSQL); rset.next(); OrdImageSignature imageSigProxy = (OrdImageSignature)rset.getORAData("image", OrdImage.getORADataFactory());
The interMedia Java Classes for servlets are utility classes used to help with the delivery and upload on media to and from the Web using the HTTP protocol. These classes make it easy to write a servlet to deliver or upload media from and to the database. These classes depend on a JDBC connection and the proxy classes that were discussed earlier in this chapter to manipulate the database media.
For delivery of media from the database, these classes can take the inter-Media proxy objects and the contents, with the correct HTTP headers populated from the media objects, as a response to an HTTP request.
For upload of media, these classes help parse the multipart/form-data request returning both text and file parts of the form. This is quite a convenience to the programmer.
The classes that are used for media retrieval are:
OrdHttpResponseHandler—. used in a Java servlet to deliver media to an HTTP client, like a browser.
OrdHttpJspResponseHandler—. used in a JSP to deliver media to an HTTP client. Note that JSP engines are not required to be capable of delivering binary data.
The classes used to upload media data into the database are:
OrdHttpUploadFormData—. this class parses the upload request so that the programmer does not have to do this. Returns parts of the request to the programmer as demanded.
OrdHttpUploadFile—. a representation of a file that is part of the upload request.
OrdMultipartFilter—. the class that implements the javax.servlet.Filter interface in servlet 2.3. A filter preprocesses the request before the servlet is called. It must be defined in the servlet parameter file for the servlet pattern being used and wraps the multipart HttpServletRe-quest in OrdMultipartWrapper.
OrdMultipartWrapper—. the wrapper around multipart/form-data request when OrdMultipartFilter is used. This wrapper wraps the servlet’s HttpServletRequest object so that multipart fields can be easily accessed without parsing (which is done in the filter).
For uploading media into the database, it should be noted that you should either use the first two classes above or the last two. You should not mix the two to avoid unnecessary reprocessing of the request.
One thing that should definitely be implemented for both methods in a real application is JDBC connection pooling. To connect to the database on every request would make access to any database data very expensive.
Note that not all JSP engines are capable of delivering nontext data. If this is the case with your servlet engine, you will be required to use a servlet. To deliver media using a JSP using OrdHttpJspResponseHandler, you need the following:
The JSP is just an endpoint of a URL. It can inspect the request and reject it. The JSP can then call a method to obtain the media. Typically this uses information in the request for finding the media, for example, an ID.
The JSP can then call one of the OrdHttpJspResponseHandler methods to send the media data. This data can be in the form of one of the media proxy objects, or a BFILE, BLOB or InputStream. If you want to send media using a BFILE, BLOB, or InputStream, you will also need to supply a MIME type and a time stamp indicating the last update time. This information is already in the media objects, so it is not necessary when sending media from interMedia proxy classes.
In the following example, we have defined a data source in OC4J data-sources.xml. Your data source may be defined differently. The definition used for the examples is as follows:
<data-source class="oracle.jdbc.pool.OracleConnectionPoolDataSource" name="jdbc/pool/OracleMediaPoolDS" location="jdbc/pool/OracleMediaPoolDS" url="jdbc:oracle:thin:@localhost:1521:orcl10g" username="scott" password="tiger" />
An example of a JSP that delivers interMedia images follows:
<%@ page import="oracle.ord.im.OrdHttpJspResponseHandler" %> <%@ page import="oracle.ord.im.OrdImage" %> <%@ page import="oracle.ord.im.OrdMediaUtil" %> <%@ page import="oracle.jdbc.pool.OracleConnectionPoolDataSource" %> <%@ page import="oracle.jdbc.pool.OraclePooledConnection" %> <%@ page import="oracle.jdbc.driver.OracleConnection" %> <%@ page import="oracle.jdbc.OraclePreparedStatement" %> <%@ page import="oracle.jdbc.OracleResultSet" %> <jsp:useBean id="handler" scope="page" class="oracle.ord.im.OrdHttpJspResponseHandler"/> <% // // Get ID of image to fetch. We could get other parameters // as well // boolean imageSent = false; String id = request.getParameter( "id" ); if ( id != null && !"".equals(id) ) { // // Use a try block to ensure the JDBC connection is // released // OracleConnection conn = null; try { // // Get a connection from the pool. The SQL would // be better // done from a factory.... // javax.naming.InitialContext ic = new javax.naming.InitialContext(); OracleConnectionPoolDataSource ds = (OracleConnectionPoolDataSource) ic.lookup("jdbc/pool/OracleMediaPoolDS"); OraclePooledConnection pc = (OraclePooledConnection) ds.getPooledConnection(); conn = (OracleConnection)pc.getConnection(); // // Here, we go to the database and select an image // from the database, Returns null if image not // found or // image column is null (not populated). // String imgSelectSQL ="select image from photos where id = "+id; OraclePreparedStatement stmt = (OraclePreparedStatement) conn.prepareStatement(imgSelectSQL); OracleResultSet rset = (OracleResultSet)stmt.executeQuery(); rset.next(); OrdImage imageProxy = (OrdImage) rset.getORAData("image", OrdImage.getORADataFactory()); rset.close(); if ( imageProxy == null) { response.setStatus( response.SC_NOT_FOUND ); return; } // // Send this image. // handler.setPageContext( pageContext ); handler.sendImage( imageProxy ); imageSent = true; } finally { // // Ensure the JDBC connection is released // if (conn != null) conn.close(); } // Go to not found error if (imageSent) return; } %> <%-- The request does not include a key to the row --%> <html lang="EN"><head><title>ExampleMediaDelivery.jsp - malformed URL</title></head> <body><h1>ExampleMediaDelivery.jsp - malformed URL</h1> </body></html>
Media delivery using servlets and the interMedia classes for JSP and servlets require that the data is obtained and then sent using the method necessary for the data type being sent.
The class that is used to deliver interMedia data from a servlet is OrdHttpResponseHandler. This class implements the send methods to send the contents of a proxy object or other types of objects, including BLOBS, to the HTTP client.
The following servlet example presents a form to the user, and when a row is found in the table, displays the thumbnail and full-size image. Please note that this servlet could have better performance by caching the more recent results of queries. Three requests are made to retrieve the data: one to populate the HTML, one to obtain the thumbnail image, and one to obtain the full-size image. Remember that each image, or multimedia, request will be a new request to the server. In this example, we use metadata from the image on the first request to populate the width and height of the image. The following is an example JSP that delivers data.
import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.Types; import java.sql.PreparedStatement; import javax.servlet.ServletException; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.naming.NamingException; import oracle.jdbc.OracleResultSet; import oracle.jdbc.pool.OracleConnectionPoolDataSource; import oracle.jdbc.pool.OraclePooledConnection; import oracle.jdbc.driver.OracleConnection; import oracle.ord.im.OrdImage; import oracle.ord.im.OrdHttpResponseHandler; import oracle.ord.im.OrdMultipartWrapper; public class deliveryServlet extends HttpServlet { OracleConnection conn = null; String servletURL = null; /** * Servlet initialization method. */ public void init( ServletConfig config ) throws ServletException { super.init(config); } /* * Get a pooled database connection */ private void getPooledConnection() throws SQLException, NamingException { javax.naming.InitialContext ic = new javax.naming.InitialContext(); OracleConnectionPoolDataSource ds= (OracleConnectionPoolDataSource) ic.lookup("jdbc/pool/OracleMediaPoolDS"); OraclePooledConnection pc = (OraclePooledConnection) ds.getPooledConnection(); conn = (OracleConnection)pc.getConnection(); // conn.setAutoCommit(false); // just query. No need for // this } /* * Process an HTTP GET request used to deliver an image column */ public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { String id = request.getParameter( "id" ); String what = request.getParameter( "what" ); if (!"image".equalsIgnoreCase(what)) what = "form"; // set a default if ("form".equalsIgnoreCase(what)) { String servletURL = request.getRequestURL().toString(); try { if (conn == null) getPooledConnection(); PrintWriter out = response.getWriter(); response.setContentType( "text/html" ); out.println( "<HTML><BODY>" + "<H1>Display Images</H1>" + "<FORM action="" + servletURL + "">" + " Enter Row ID:<INPUT type="text" name="id" /><BR/>"); if (id != null) { PreparedStatement stmt = conn.prepareStatement("select description,"+ "location, " + "thumb, image "+ " from photos where id = ?" ); stmt.setString( 1, id ); OracleResultSet rset = (OracleResultSet)stmt.executeQuery(); // // Fetch the row from the result set. // if ( rset.next() ) { // // Get columns from query // String location = rset.getString(1); String description = rset.getString(2); OrdImage thumb = (OrdImage)rset.getORAData(3, OrdImage.getORADataFactory()); OrdImage img = (OrdImage)rset.getORAData(4, OrdImage.getORADataFactory()); out.println("<B>Description: </B>" + description+ "<BR/>"); out.println("<B>Location: </B>" + location+ "<BR/>"); if (thumb != null && thumb.getMimeType().startsWith("image/")) if (thumb != null && thumb.getMimeType().startsWith("image/")) { String thmbWidthStr = thumb.getWidth() == 0 ? "" : "WIDTH="" + thumb.getWidth() + "" "; String thmbHeightStr = thumb.getHeight() == 0 ? "" : "HEIGHT="" + thumb.getHeight() + "" "; out.println("<B>Thumbnail: </B>" + "<IMG SRC="" + servletURL + "?id=" + id + "&what=image&col=thumb" " + thmbWidthStr + thmbHeightStr + "/>" ); } if (img != null && img.getMimeType().startsWith("image/")) { String imgWidthStr = img.getWidth() == 0 ? "" : "WIDTH="" + img.getWidth() + "" "; String imgHeightStr = img.getHeight() == 0 ? "" : "HEIGHT="" + img.getHeight() + "" "; out.println("<B>Image: </B>" + "<IMG SRC="" + servletURL + "?id=" + id + "&what=image&col=image" " + imgWidthStr + imgHeightStr + "/>" ); } } else { // // Print not found // out.println("<H2>Row with ID"" + id + "" Not Found</H2><BR/>"); } } out.println("</FORM></BODY></HTML>"); } catch (Exception e) { conn = null; // Get another connection next time. throw new ServletException(e); } } else { try { if (conn == null) getPooledConnection(); String col = request.getParameter("col"); if (col == null) col = "thumb"; //default to the thumbnail column PreparedStatement stmt = conn.prepareStatement( "select " + col + " from photos where id = ? " ); stmt.setString( 1, id ); OracleResultSet rset = (OracleResultSet)stmt.executeQuery(); // // Fetch the row from the result set. // if ( rset.next() ) { // // Get the OrdImage object from the result set. // OrdImage img = (OrdImage)rset.getORAData(1, OrdImage.getORADataFactory()); // // Create an OrdHttpResponseHandler object, then // use it to get // the image from the database and deliver it to // the browser. // OrdHttpResponseHandler handler = new OrdHttpResponseHandler( request, response ); handler.sendImage( img ); } else { // // Row not found, return a suitable error. // response.setStatus( response.SC_NOT_FOUND ); } // // Close the result-set and the statement. // rset.close(); stmt.close(); } catch (Exception e) { conn = null; // Get another connection next time. throw new ServletException(e); } } } }
The media upload classes are convenience classes to help parse a request with media in a servlet environment. It can be quite a bit of code to parse a multipart/form-data POST request. These classes perform this parsing for you. If you want total control over the parsing of the form, you are not required to use these classes to upload media data. You can parse the request yourself.
Uploading interMedia media over HTTP using Java is done through a servlet. To upload data to the database you need:
A Web page form that
Sends the form data in multipart/form-data format in an HTTP POST request.
Has at least one input field of type file.
A servlet that will accept the form from the client.
This servlet will have a connection to the database.
This servlet will either create a new row in the database for the media or populate an existing row.
There are two techniques that you can use with the convenience classes to upload media. The first is to have all the code in the servlet to parse the multipart/form-data POST request. The second is to make use of the servlet filter provided by interMedia. This will parse the request before the servlet code is called and wrap the request in an OrdMultipartWrapper object that has all the functionality of an HttpServletRequest object plus much of the functionality that OrdHttpUploadFormData has. The first technique is called the request parsing technique, the second is called the filter technique.
One important note when uploading media data from the Web is to know the maximum size of the data. If this data is very large, perhaps a 10-gigabyte movie, it is certainly best for the servlet to cache this movie in a file rather that Java’s virtual memory. You can set the limit of the Java virtual memory use with either technique. In the filter technique, you would call the OrdHttpUploadFormData setMaxMemory method to set the maximum size of virtual memory use and the directory to use for temporary files. For the filter technique, these settings are set as part of the filter configuration. These parameters should always be set to prevent exhausting Java memory event if you don’t think the data will ever be that large. A mistake, or mischief, could cause problems for your Web server.
One nice feature of using these classes to upload files into interMedia database objects is that they populate the MIME type of the object from the HTTP request before attempting to get properties from the binary data. In the case that the binary data is not recognized by interMedia media parsing the MIME type will still be set to an appropriate value. A browser typically sets the MIME type of data it is uploading based on the file extension.
The result of using either the parsing technique or filtering technique is the standard parameters from a form request in string objects and the file parts of the form in OrdHttpUploadFile objects. The OrdHttpUploadFile objects are used to provide information about the file, such as MIME type, file name, and length, and populate the database with file data from the request in interMedia proxy objects or a standard database BLOB.
To upload media from a browser requires an appropriate HTML form. A very simple example of an HTML form that uploads an image follows.
<HTML> <BODY> <FORM action="http://localhost:8888/servlet/uploadServlet" name="uploadForm" method="post" enctype="multipart/form-data"> <P> Location? <INPUT type="text" name="location"/><BR/> Description? <INPUT type="text" name="description"/><BR/> Image File? <INPUT type="file" name="photo"/><BR/> <INPUT type="submit" value="Submit" /> </P> </FORM> </BODY> </HTML>
In the following example a database sequence object is created to assign an ID number to added media. It is preferable to use sequences rather than the maximum ID plus one because two users may be trying to insert media at the same time. With a sequence, we can guarantee that simultaneous update requests do not try to use the same ID. This sequence is created with the following SQL statement.
SQL> create sequence photos_sequence;
This example is a servlet that handles the upload request from the preceding form. This servlet works with either the filter technique or parsing using OrdHttpUploadFormData. To use the filter method, the filter must be put into the servlet filter chain. Each technique will be described in more detail in subsequent sections, but a simple example servlet follows.
import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.Types; import javax.servlet.ServletException; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.naming.NamingException; import oracle.jdbc.OracleCallableStatement; import oracle.jdbc.OraclePreparedStatement; import oracle.jdbc.OracleResultSet; import oracle.jdbc.pool.OracleConnectionPoolDataSource; import oracle.jdbc.pool.OraclePooledConnection; import oracle.jdbc.driver.OracleConnection; import oracle.ord.im.OrdImage; import oracle.ord.im.OrdHttpUploadFile; import oracle.ord.im.OrdHttpUploadFormData; import oracle.ord.im.OrdHttpResponseHandler; import oracle.ord.im.OrdMultipartWrapper; public class uploadServlet extends HttpServlet { OracleConnection conn = null; OrdHttpUploadFormData formData = null; /** * Servlet initialization method. */ public void init( ServletConfig config ) throws ServletException { super.init(config); } /* * Get a pooled database connection */ private void getPooledConnection() throws SQLException, NamingException { javax.naming.InitialContext ic = new javax.naming.InitialContext(); OracleConnectionPoolDataSource ds = (OracleConnectionPoolDataSource) ic.lookup("jdbc/pool/OracleMediaPoolDS"); OraclePooledConnection pc = (OraclePooledConnection) ds.getPooledConnection(); conn = (OracleConnection)pc.getConnection(); conn.setAutoCommit(false); // Update or put Media, need this } /* * Process an HTTP Post request used to upload a new photo * into the album */ public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { String description = null; String location = null; OrdHttpUploadFile photo = null; if (request instanceof OrdMultipartWrapper) { // // This request was parsed by OrdMultipartFilter, // use it. // OrdMultipartWrapper ordMultiPartWrapper = (OrdMultipartWrapper)request; // // Get the description, location and photo from the // wrapper // description = ordMultiPartWrapper.getParameter( "description" ); location = ordMultiPartWrapper.getParameter( "location" ); photo = ordMultiPartWrapper.getFileParameter( "photo" ); } else { // Parse the data using OrdHttpUploadFormData // // Create an OrdHttpUploadFormData object. // formData = new OrdHttpUploadFormData( request ); if ( !formData.isUploadRequest() ) { return; } // // Parse the multipart/form-data message. // (10 meg max before using file), use system // temp directory formData.setMaxMemory(10 (1024*1024), null); formData.parseFormData(); // // Get the description, location and photo. // description = formData.getParameter( "description" ); location = formData.getParameter( "location" ); photo = formData.getFileParameter( "photo" ); } // // Make sure a valid image file was provided. // if ( photo == null || photo.getOriginalFileName() == null || photo.getOriginalFileName().length() == 0 || photo.getContentLength() == 0 ) { return; } try { if (conn == null) getPooledConnection(); //Get connection if needed // // Get a value for the ID column of the new row // OraclePreparedStatement stmt = (OraclePreparedStatement)conn.prepareStatement( "select photos_sequence.nextval from dual" ); OracleResultSet rset = (OracleResultSet)stmt.executeQuery(); if ( !rset.next() ) throw new ServletException( "new ID not found" ); String id = rset.getString( 1 ); rset.close(); stmt.close(); // // Prepare and execute a SQL statement to insert the // new row. // OracleCallableStatement cstmt = (OracleCallableStatement) conn.prepareCall ( "begin " + "insert into photos t " + " (id, description, location, image," + "thumb) " + "values (?, ?, ?, ORDImage.init()," + "ORDImage.init())" + " returning rowid, t.image, t.thumb into " + "?, ?, ? ; " + "end;"); cstmt.setString( 1, id ); cstmt.setString( 2, description ); cstmt.setString( 3, location ); cstmt.registerOutParameter(4, Types.VARCHAR); // rowid cstmt.registerOutParameter(5, Types.STRUCT, "ORDIMAGE"); // image cstmt.registerOutParameter(6, Types.STRUCT, "ORDIMAGE"); // thumb cstmt.executeUpdate(); cstmt.close(); String rowid = cstmt.getString(4); // Obtain the proxy objects OrdImage image = (OrdImage) cstmt.getORAData(5, OrdImage.getORADataFactory()); OrdImage thumb = (OrdImage) cstmt.getORAData(6, OrdImage.getORADataFactory()); cstmt.close(); // // Load the photo into the database and set // the properties. // photo.loadImage( image ); if (formData != null) formData.release(); // Release any resources // // Try to copy the full-size image and process it to // create the thumb-nail. This may not be // possible if the image format is // not recognized. // try { image.processCopy( "maxScale=50,50", thumb ); } catch ( SQLException e ) { thumb.deleteContent(); thumb.setContentLength( 0 ); } // // Prepare and execute a SQL statement to update the // full-size and // thumb-nail images in the database. // stmt = (OraclePreparedStatement)conn.prepareStatement( "update photos set image = ?, thumb = ? where" + "rowid = ?" ); // stmt.setORAData( 1, image ); stmt.setORAData( 2, thumb ); stmt.setString( 3, rowid ); stmt.execute(); stmt.close(); // // Commit the changes. // conn.commit(); } catch (Exception se) { conn = null; // We may need a new connection if (formData != null) formData.release();//Release any resources throw new ServletException(se); } // Print a response page (or we could redirect here PrintWriter out = response.getWriter(); response.setContentType( "text/html" ); out.println( "<html><body>" + "<h1>Upload Sucessful</h1>" + "<input type="button" onclick= "history.go(-1)" "+ "value="back"/>" + "</body></html>"); } }
The following example servlet shows how the multipart/form-data request is parsed.
// Parse the data using OrdHttpUploadFormData // // Create an OrdHttpUploadFormData object. // [1] OrdHttpUploadFormData formData = new OrdHttpUploadFormData( request ); [2] if ( !formData.isUploadRequest() ) { return; } // // Parse the multipart/form-data message. // [3] formData.parseFormData(); // // Get the description, location and photo. // [4] description = formData.getParameter( "description" ); location = formData.getParameter( "location" ); [5] photo = formData.getFileParameter( "photo" );
To receive media and other form data from an HTTP request, the following steps are taken:
To use a filter in J2EE, the filter parameters must be set in web.xml for the Web application the servlet is running from. You need to specify the filter and filter parameters, and then specify which servlet will use the filter. The interMedia HTTP filter only filters requests. An example snippet from web.xml is shown below that will cause the filter to be invoked, parsing the multipart/form-data, before our servlet is invoked.
<filter> <filter-name>ordMultipartFilter</filter-name> <filter-class>oracle.ord.im.OrdMultipartFilter </filter-class> <init-param> <param-name>tempDir</param-name> <param-value>d: emp</param-value> </init-param> <init-param> <param-name>maxMemory</param-name> <param-value>10000000000</param-value> </init-param> </filter> <filter-mapping> <filter-name>ordMultipartFilter</filter-name> <url-pattern>/servlet/tstPhotoAlbumServlet </url-pattern> </filter-mapping>
When the above code is set correctly, the section of the example servlet that uses the filter method will be used. This section is:
[1] if (request instanceof OrdMultipartWrapper) { // // This request was parsed by OrdMultipartFilter, use it. // [2] OrdMultipartWrapper ordMultiPartWrapper = (OrdMultipartWrapper)request; // // Get the description, location and photo from the wrapper // [3] description = ordMultiPartWrapper.getParameter( "description" ); location = ordMultiPartWrapper.getParameter( "location" ); [4] photo = ordMultiPartWrapper.getFileParameter( "photo" ); }
When using OrdMultipartWrapper to receive media data and other form data from an HTTP request, the following stepts are taken:
Test if the HttpServletRequest has been wrapped in OrdMultipartWrapper by OrdMultipartFilter.
Cast the request to OrdMultipartWrapper for convenience.
Obtain the text parts of the request as string objects.
Obtain the file part of the request as an OrdHttpUploadFile object.
Oracle interMedia provides a rich set of features for the J2EE environment. It has support for servlets, JSPs and representing database interMedia objects as Java objects. interMedia can also be used effectively with Oracle JDeveloper, as this application development tool can recognize interMedia objects.