Académique Documents
Professionnel Documents
Culture Documents
Abstract:
This tutorial introduces how to use Jdeveloper 10g, the ADF BC framework and the Oracle 9i RDBMS in order to build a little web application enabling to upload and download files. These files will be kept into the database as BLOB and the interfaces of the app will let to organize these records depending on their extension and a type chosen by the user. An administrator will be able to manage the content of this type list and too the extension and their associated mime-type. Prior to follow these instructions, assume to have successfully installed Jdeveloper (Jdev) 10g and Oracle 9i, and to have created a database (DB) instance.
Content:
I. Building the model layer .................................................................................................... 2 Generating the application skeleton ............................................................................... 2 Making the DB schema .................................................................................................. 3 i. Connecting the DB ..................................................................................................... 3 ii. Building the DB schema ............................................................................................ 4 c. Organizing and configuring the app module.................................................................. 8 i. Generating the Business Components........................................................................ 8 ii. Organizing the Business Components........................................................................ 9 iii. Configuring the Business Components ................................................................ 10 II. Building the controller layer ............................................................................................ 13 a. Generating the interface skeleton ................................................................................. 13 i. Configuring the UserInterface project .................................................................. 13 ii. Building and using the faces-config.xml .............................................................. 13 iii. Adding the source files......................................................................................... 15 b. Coding the needed methods and functions................................................................... 16 i. Controlling the application administrator tasks ....................................................... 16 ii. Controlling the library tasks ..................................................................................... 18 III. Designing the interfaces ............................................................................................... 24 a. Generating the administration page.............................................................................. 24 i. Management of the type list ..................................................................................... 24 ii. Management of the extensions and the mime-types ................................................ 27 b. Generating the library page .......................................................................................... 28 i. Displaying the files................................................................................................... 28 ii. Enabling to upload new files .................................................................................... 29 c. Binding the controllers and refining the display .......................................................... 30 i. On the administration.jspx page ........................................................................... 30 ii. On the Library.jspx page ...................................................................................... 32 a. b.
The creation wizard launches the form enabling the creation of a new project into the app; Enter DataModel and accept the default directory. A simple setting to add is the default package name. For that right click on the new created project and select Project Properties. Select the Project Content facet and change the text field at the bottom of the form: Default package which should be medlib has to be change for medlib.datamodel.
Now add a Foreign Key: Referenced DOCS on constraint DOC_PK and make matching DETAIL_DOC in the local column to DOC_ID in the referenced column on DOCS. Finally choose OK. Add still two tables EXTENSIONS and TYPES, and then edit DETAILS to add their ID in the foreign keys.
When schema is complete, it has to be reconciled to the DB. Automatically, Jdev has built the Offline Database Sources which are an offline copy of the elements contained in the schema. The folder containing these sources should have name MYSCHEMA. Right click on it and choose Generate or Reconcile Objects: Step 1: Switch the 4 table elements except DETAILS (to assume the other tables will exist before trying to create the foreign keys) to the Selected panel. The tables have to be created before trying to generate the sequence. Step 2: Choose CREATE Objects. Functions REPLACE and ALTER can be used when objects already exists in the DB. Step 3: Uncheck Generate SQL Scripts. Checking this option has no consequences on this tutorial and can be done for having a look on the way of SQL generates the DB objects. Step 4: Check Perform Operations Against Database. Choose for Connection the MediaLibraryConnection and for Schema the name given to the DB user which is TIF. Step 5: Check the summary before to accept it. Until clicking on End, the Oracle DB schema TIF is still empty and that action will populate it. Now, complete the table creation by clicking on End and check the pop up message. This message confirms the table creation. Step 6: Now, reconcile the last table DETAILS by repeating step 1 to 5 with switching only this table. Step 7: Finally, reconcile the sequences by repeating the step 1 to 5 with switching only the sequences. NOTE: Its possible to reconcile offline objects to the DB by right clicking on the schema and selecting Generate > Data Definition Language For Database.
TRIGGER ASSIGN_DETAIL_ID BEFORE INSERT ON DETAILS FOR EACH ROW BEGIN IF :NEW.DETAIL_ID IS NULL OR :NEW.DETAIL_ID < 0 THEN SELECT DETAIL_SEQ.NEXTVAL INTO :NEW.DETAIL_ID FROM DUAL; END IF; END; TRIGGER ASSIGN_EXTENSION_ID BEFORE INSERT ON EXTENSION FOR EACH ROW BEGIN IF :NEW.EXTENSION_ID IS NULL OR :NEW.EXTENSION_ID < 0 THEN SELECT EXTENSION_SEQ.NEXTVAL INTO :NEW.EXTENSION_ID FROM DUAL; END IF; END; TRIGGER ASSIGN_TYPE_ID BEFORE INSERT ON TYPE FOR EACH ROW BEGIN IF :NEW.TYPE_ID IS NULL OR :NEW.TYPE_ID < 0 THEN SELECT TYPE_SEQ.NEXTVAL INTO :NEW.TYPE_ID FROM DUAL; END IF; END;
Build the three others triggers by adapting the preceding steps. In the Connection Navigator window, the existing objects should match the following screen:
Jdev enables, thanks to the Application Development Framework Business Components (ADF BC), to build very quickly and easily the model layer. Right click on the datamodel package and select new Business Components from Tables: Step 1 creating the EO: Click on the Query button, and switch all the 4 tables into the Selected panel. Step 2 creating the Updatable VO: Switch all the 4 tables into the Selected panel. Step 3 creating the Read-only VO: No read-only VO has to be created. Step 4 creating the AM: Check the box Application Module and enter MedLibModule in the field Name. Step 5 creating the Business Component Diagram: uncheck the box, and click on Next. Step 6 summary: The displayed summary should match the following, if not, cancel the creation and start again the creation at step 1:
The first task to perform is to fix the ID of each EO on the DBSequence type: Double click on the EO, expand the attribute node and select the ID attribute. Use the Type drop down list to find DBSequence. Keep the other default values, click on Apply then on Ok. Repeat these simple operations for each one of the EO. Now, change the labels to customize the future interface displays: For the Extensions and the Types EO, double click on the EO, expand the attribute node and select one of the non ID attribute. Click on the Control Hints tab. In the Label Text field, enter Extension Name, Type Name, Mimetype or Type Definition depending on the selected attribute. In the Display width field, type 25 (except for the Type Definition where this value has to be fixed to 50) Repeat these simple operations until have fixed Label Text and Display width for each one of the four attributes. Now repeat these steps for the Details EO. DetailName should have label File Name and width = 50. DetailDate take 10 and Upload Date for values.
NOTE: Its important to save regularly the current progress. The use of Make and Rebuild is another think to keep in mind; java has to be compiled to work.
NOTE: The yellow warning signals mean these pages dont yet exist. Have a look on the tab Source, the underlining lines have the same signification.
Right click on the WEB-INF project and choose New. Drill down the Web Tier node, select the JSF and choose JSF JSP: Step 1: Enter template.jspx in the File Name field and add \template to the default entry in Directory Name. Assume to have checked the JSP Document box Step 2: Check Do Not Automatically Step 3: Assume you have selected (in the right panel), the ADF Faces Components, ADF Faces HTML, JSF Core 1.0 and JSF HTML 1.0 libraries. Step 4: Here, you could add a personal touch, but just click on Finish. The new template appears in the principal window. Drop on it the PanelPage component from the ADF Faces Core elements (from Component Palette). Then Drop a MenuBar and two PanelGroup components. These different containers will be use in the pages.
Repeat these steps two times with using JSFUtils and FileOperations for the class names. Copy the content from appendix to the corresponding Java class.
<!-- Maximum memory per request (in bytes) --> <context-param> <param-name>oracle.adf.view.faces.UPLOAD_MAX_MEMORY</param-name> <!-- Use 5,000K --> <param-value>5120000</param-value> </context-param> <!-- Maximum disk per request (in bytes) --> <context-param> <param-name>oracle.adf.view.faces.UPLOAD_MAX_DISK_SPACE</param-name> <!-- Use 500,000K --> <param-value>512000000</param-value> </context-param> <!-- Temporary storage directory --> <context-param> <param-name>oracle.adf.view.faces.UPLOAD_TEMP_DIR</param-name> <param-value>/tmp/ADFUploads/</param-value> </context-param> <filter> <filter-name>adfBindings</filter-name> <filter-class>oracle.adf.model.servlet.ADFBindingFilter</filter-class> </filter> <filter-mapping> <filter-name>adfBindings</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>adfBindings</filter-name> <url-pattern>*.jspx</url-pattern> </filter-mapping>
The new class is created but has to be declared into the faces-config.xml to be used: Step 1: Open the faces-config.xml in the principal window by a double click on it in the Application Navigator window. Step 2: With the Overview tab selected, choose the Managed beans and click on the button New. Step 3: Enter LibraryBean in the Name field and userinterface.view.backing.LibraryBean in the field Class. This second text can be obtained by using the Browse button and selecting the class LibraryBean. Finally keep the Request scope and the box Generate checked, then press Ok. This bean will have to be bound to the interface context. To enable this, more declarations are needed: With the LibraryBean selected into the Managed Beans panel, expand the Managed Properties panel with the black array and add a new property with the New button. Insert bindings in the Name field and accept the form. Now, with the new Managed properties selected, change the Value in the Managed property panel from null to #{bindings}.
Right click on the word bindings, then choose Generate Accessors. Check all the boxes in order to generate the getters and the setters for the three new attributes. The code editor automatically imports the corresponding libraries, also the following lines have been created:
import oracle.adf.view.faces.component.core.layout.CorePanelGroup; import oracle.binding.BindingContainer;
Generate all the corresponding accessors. The code editor has imported the following class:
import oracle.adf.view.faces.component.core.layout.CorePanelHeader; import oracle.adf.view.faces.component.core.layout.CorePanelBox;
public String libraryUploadSwtichDisplay() { if (this.getLibraryUploadTypeSelectionPanelheader().isRendered()) { this.getLibraryUploadTypeSelectionPanelheader().setRendered(false); this.getLibraryUploadFileSelectionPanelHeader().setRendered(true); } else { this.getLibraryUploadTypeSelectionPanelheader().setRendered(true); this.getLibraryUploadFileSelectionPanelHeader().setRendered(false); } return null; }
To confirm the choice and going to the page enabling to select the file to upload, the simplest solution is to use the preceding libraryUploadSwitchDisplay function. Now add two functions to extract the name and the extension of the selected file:
/** * Enable to read the name a document by cuting its extension * @param filename The complete name of a document * @return String Return the name of the document without its extension */ public static String findExactName(String filename) { String[] tests = filename.toLowerCase().split("\\."); Integer i = null; for (i = 1; i < tests.length - 1; i++) { tests[i] = tests[i - 1].concat("." + tests[i]); } return tests[tests.length - 2]; } /** * Enable to read the extension of a document by cuting its name * @param fileName The complete extension of a document with the initial dot * @return String Return the extension of the document without its name */ public static String getExtensionFromFileName(String fileName) { String[] tests = fileName.toLowerCase().split("\\."); return "." + tests[tests.length - 1]; }
To simplify the use of the library, its very useful to control the extensions of the uploaded files and to create them if they are not yet existing:
/** * Add an extension to the existing one in the database * @param fileExtension * @return the new extension Id */ public Integer createAnExtension(String fileExtension) { DBSequence newExtensionId = new DBSequence("EXTENSION_SEQ", ADFUtils.getApplicationModule("MedLibModule")); DCBindingContainer dcBindings = (DCBindingContainer)ADFUtils.findBindingContainer(getBindings(), "app_administrationPageDef"); DCIteratorBinding docExtensionIter = dcBindings.findIteratorBinding("ExtensionsView1Iterator"); docExtensionIter.getViewObject().clearCache(); docExtensionIter.getViewObject().executeQuery(); Row newRow = docExtensionIter.getViewObject().createRow(); newRow.setAttribute("ExtensionId", newExtensionId); newRow.setAttribute("ExtensionName", fileExtension); docExtensionIter.getViewObject().insertRow(newRow); OperationBinding operationBinding = dcBindings.getOperationBinding("Commit"); Object result = operationBinding.execute(); return new Integer(newExtensionId.toString()); } /** * Test if an extension name exists in an iterator based on the extension Db table * @param extensionName, the extension name to look for * @return Boolean, True if the extension name is already existing in the database */ public Boolean extensionExist(String extensionName) { Boolean result = false; String testExtension = null; DCBindingContainer dcBindings = (DCBindingContainer)ADFUtils.findBindingContainer(getBindings(), "app_administrationPageDef"); DCIteratorBinding docExtensionIter = dcBindings.findIteratorBinding("ExtensionsView1Iterator"); ViewObject docExtensionVO = docExtensionIter.getViewObject(); docExtensionVO.clearCache(); docExtensionVO.executeQuery(); if (docExtensionVO.getEstimatedRowCount() != 0) { testExtension = docExtensionVO.first().getAttribute("ExtensionName").toString(); if (testExtension.matches(extensionName)) { result = true; } if (docExtensionVO.getEstimatedRowCount() > 1) { for (Integer i = 1; i < docExtensionVO.getEstimatedRowCount(); i++) { testExtension = docExtensionVO.next().getAttribute("ExtensionName").toString(); if (testExtension.matches(extensionName)) { result = true; }}} } return result; }
/** * Find the extension ID corresponding to the given extension name * @param extensionName, the extension name to look for * @return Integer, the extension ID of interest */ public Integer findAnExtensionFromName(String extensionName) { Integer resultingExtensionId = 0; String testExtension = null; DCBindingContainer dcBindings = (DCBindingContainer)ADFUtils.findBindingContainer(getBindings(), "app_administrationPageDef"); DCIteratorBinding docExtensionIter = dcBindings.findIteratorBinding("ExtensionsView1Iterator"); ViewObject docExtensionVO = docExtensionIter.getViewObject(); docExtensionVO.clearCache(); docExtensionVO.executeQuery(); if (extensionExist(extensionName.toLowerCase())) { if (docExtensionVO.getEstimatedRowCount() != 0) { testExtension = docExtensionVO.first().getAttribute("ExtensionName").toString().toLowerCase(); if (testExtension.matches(extensionName)) { resultingExtensionId = new Integer(0).valueOf(docExtensionVO.getCurrentRow().getAttribute("ExtensionId").toString ().toLowerCase()); } } if (docExtensionVO.getEstimatedRowCount() > 1) { for (Integer i = 1; i < docExtensionVO.getEstimatedRowCount(); i++) { testExtension = docExtensionVO.next().getAttribute("ExtensionName").toString().toLowerCase(); if (testExtension.matches(extensionName)) { resultingExtensionId = new Integer(0).valueOf(docExtensionVO.getCurrentRow().getAttribute("ExtensionId").toString ().toLowerCase()); } } } } return resultingExtensionId; }
Finally it misses only the functions creating the BLOB with its details and the function which will be called from the interface:
public Integer createTheDocumentBlob(BlobDomain file) { DBSequence newBlobId = new DBSequence("DOC_SEQ", ADFUtils.getApplicationModule("MedLibModule")); DCIteratorBinding docBlobIter = ((DCBindingContainer)getBindings()).findIteratorBinding("DocsView1Iterator"); ViewObject docBlob = docBlobIter.getViewObject(); Row newRow = docBlob.createRow(); newRow.setAttribute("DocId", newBlobId); newRow.setAttribute("DocBlob", file); docBlob.insertRow(newRow); OperationBinding operationBinding = getBindings().getOperationBinding("Commit"); Object result = operationBinding.execute(); return new Integer(newBlobId.toString()); } public void createTheDocumentDetails(Integer docBlob) { DCIteratorBinding docDetailsIter = ((DCBindingContainer)getBindings()).findIteratorBinding("AllFilesAndDetails1Iterator"); ViewObject allFilesAndDetails = docDetailsIter.getViewObject(); Row newRow = allFilesAndDetails.createRow(); newRow.setAttribute("DetailId", -1); newRow.setAttribute("DetailName", findExactName(uploadedFile.getFilename())); Integer docExtension = 0; if (extensionExist(this.getExtensionFromFileName(uploadedFile.getFilename()))) { docExtension = this.findAnExtensionFromName(this.getExtensionFromFileName(uploadedFile.getFilena me())); } else { docExtension = this.createAnExtension(this.getExtensionFromFileName(uploadedFile.getFilename())); } newRow.setAttribute("DetailExtension", docExtension); newRow.setAttribute("DetailType", findTheSelectedTypeFromList4UploadingNewFile()); newRow.setAttribute("DetailDoc", docBlob); java.sql.Date docDate = new java.sql.Date(new Date().getTime()); newRow.setAttribute("DetailDate", new oracle.jbo.domain.Date(docDate)); allFilesAndDetails.insertRow(newRow); OperationBinding operationBinding = getBindings().getOperationBinding("Commit"); Object result = operationBinding.execute(); }
public String uploadAction() { try { file = FileOperations.writeToBlobDomain(uploadedFile); Integer docBlob = createTheDocumentBlob(file); createTheDocumentDetails(docBlob); uploadedFile.dispose(); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } return librarySwitchDisplay(); } public void onUploading(ValueChangeEvent evt) { uploadedFile = (UploadedFile)evt.getNewValue(); fileName = uploadedFile.getFilename(); }
Generate the corresponding accessors. The code editor has imported the following class:
import oracle.adf.view.faces.component.core.output.CoreOutputText;
public String downloadAction() { DCIteratorBinding blobIter = ADFUtils.getIterator("AllFilesAndDetails1"); Row r = blobIter.getCurrentRow(); BlobDomain file = (BlobDomain)r.getAttribute("DocBlob"); String fileName = (String)r.getAttribute("DetailName"); String fileExtension = (String)r.getAttribute("ExtensionName"); String fileMimeType = (String)r.getAttribute("ExtensionMimetype"); FileOperations.downloadFile(fileName, fileExtension, fileMimeType, file); //ADFUtils.executeOperation("app_LibraryPageDef", "Execute"); return libraryDownloadSwitchDisplay(); }
Under the Table facets are present different facets. The Selection facet, into which have been dropped the two buttons, From the Data Control Palette: From the Operations folder, drag and drop the Create action on the actions facet. Choose ADF Command Button. Right click on the new command button and choose Edit Command Button. In the Select an action drop down list, select Create insert.
Its possible to run the page by a right click on the page then select Run. The actions delete and create can be used, but there is no way for saving the data until now. Actually these actions enable to modify the collection build from the content of the database and the Execute action will submit the changes to the database, but the transactions have to be committed to the database to be saved permanently.
Its possible to run the page by a right click on the page then select Run. When using one of the Execute, Delete or Create actions, the Commit and Rollback buttons become available. These buttons enable to confirm or to declare null the changes.
In the Structure window, the page should now look like the following:
Repeat these operations for the second panel group: Find and select the second PanelGroup In the Property inspector, enter AdministrationExtensionPanel in the Id field Click in the Binding field then on the little button with the three dots. A wizard, enabling to associate the panel group component with a bean, appears. Select LibraryBean in the first list and administrationExtensionsPanelGroup in the second. The field should take the value: #{LibraryBean.administrationExtensionsPanelGroup}. Now add the function under the command button: Expand the menuBar and select the commandButton 1 In the Property inspector, click on the Action property then on the little button with the three dots. A wizard appears to associate a button to an action declared in a bean. Select LibraryBean in the first list and administrationSwitchDisplay in the second. The field should take the value: #{LibraryBean.administrationSwitchDisplay}.
Using the Structure and the Property Inspector windows: Find and select the command button commandButton 1 In the Property inspector, click on the Text property then on the little button with the three dots. A wizard appears to build the button label. Expand JSF Managed Beans > LibraryBean > administrationTypesPanelGroup and double click on the rendered attribute. Finally modify the proposed expression to obtain the following:
#{LibraryBean.administrationTypesPanelGroup.rendered? 'Go to Extensions Management':'Go to Types Management'}
Do the same for the three buttons on the extension table: Find and select the command button Create1and rename it in the property inspector with Create. Find Execute1 and Delete1, then change them for Execute and Delete.
Finally, make the document extensions management panel disappearing: Select the second PanelGroup. From the Properties Inspector, find the attribute Rendered and set it to false.
Using the Structure and the Property Inspector windows: Find and select the first PanelGroup In the Property inspector, enter LibraryDownloadPanelGroup in the Id field Click in the Binding field then on the little button with the three dots. A wizard, enabling to associate the panel group component with a bean, appears. Select LibraryBean in the first list and libraryDownloadPanelGroup in the second. The field should take the value: #{LibraryBean.libraryDownloadPanelGroup}. Repeat these steps with the second PanelGroup. Use LibraryUploadPanelGroup in the Id field and complete the Binding to obtain #{LibraryBean.libraryUploadPanelGroup}. Expand the first PanelGroup, and select the first PanelHeader. Set the properties Id and Binding to LibraryDownloadSelectionPanelHeader and #{LibraryBean.libraryDownloadSelectionPanelHeader}. Select the second PanelHeader and set Id and Binding to LibraryDownloadConfirmationPanelHeader and #{LibraryBean.libraryDownloadConfirmationPanelHeader}. Expand the second PanelGroup, and select the first PanelBox. Set the properties Id and Binding to LibraryUploadTypeSelectionPanelbox and #{LibraryBean.LibraryUploadTypeSelectionPanelbox}. Select the second PanelBox and set Id and Binding to LibraryUploadFileSelectionPanelbox and #{LibraryBean.LibraryUploadFileSelectionPanelbox}. Now add the functions under the command buttons: Expand the menuBar and select the commandButton 2 In the Property inspector, click on the Action property then on the little button with the three dots. A wizard appears to associate a button to an action declared in a bean. Select LibraryBean in the first list and librarySwitchDisplay in the second. The field should take the value: #{LibraryBean. librarySwitchDisplay}. Repeat these steps to trigger the actions libraryDownloadSwitchDisplay and libraryUploadSwtichDisplay with respectively the command buttons commandButton 5 and commandButton 3 Do it again for have the actions #{LibraryBean.downloadAction}, #{LibraryBean.uploadAction}, #{LibraryBean.selectFileToDownload} and #{LibraryBean.libraryUploadSwtichDisplay} respectively on the buttons commandButton 6, commandButton 4, commandButton 1 and Submit.
The outputText component which will display the name of the selected file to download needs to be linked to the bean: Find the outputText1 from the Structure window. In the Property inspector, enter NameOfSelectedFile4Download in the attribute id. In the binding attribute select LibraryBean and nameOfSelectedFile4Download to obtain the value #{LibraryBean.nameOfSelectedFile4Download}. The last action to bind is the one which enables to automatically keeping up-to-date the UploadedFile attribute in the bean with the selected file: Find the af:InputFile from the Structure window. In the Properties Inspector, find the ValueChangeListener attribute and set its value to #{LibraryBean.onUploading}.
Finally, your application is almost ready to use. The last step is to avoid the display of some panels at the first use of the application. The Rendered attribute has to bet set to false for the second PanelBox of the second PanelGroup, the second PanelGroup and the second panelHeader of the first PanelGroup.
You can now run your application. To access from one page to the other change in the internet browser address field Library.jspx by administration.jspx or administration.jspx by Library.jspx depending on the current displayed page. Lot of improvements can be added but that will be the subject for next tutorials.