Red Hat Web Application Framework 6.
Red Hat Web Application Framework 6.1: WAF Developer Guide Copyright © 2004 by Red Hat, Inc. Red Hat, Inc. 1801 Varsity Drive Raleigh NC 27606-2072 USA Phone: +1 919 754 3700 Phone: 888 733 4281 Fax: +1 919 754 3701 PO Box 13588 Research Triangle Park NC 27709 USA rhea-dg-waf-en(EN)-6.1-Print-RHI (2004-03-29-T16:20-0800) Copyright © 2004 by Red Hat, Inc. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, V1.
Table of Contents Introduction to the WAF Developer Guide........................................................................................ i 1. Assumptions About WAF Developers ................................................................................... i 2. Document Conventions.......................................................................................................... i 3. Code Presentation Conventions .............................................................................
8.4. Build Tools........................................................................................................... 50 8.5. Modeling Your Application ................................................................................. 50 8.6. Persistent Object Types........................................................................................ 51 8.7. Java Domain Objects ........................................................................................... 51 8.8.
B.3. Coding Style...................................................................................................... 183 B.4. Constraint Naming Standards............................................................................ 183 C. Java Standards .................................................................................................................. 187 C.1. WAF Standards.................................................................................................. 187 C.2.
Introduction to the WAF Developer Guide The Red Hat Web Application Framework is a platform for writing database-backed web applications in Sun’s Java®. Applications leverage Web Application Framework APIs to enable the authoring of persistent structured data and to retrieve and display the data as content. The framework also integrates services such as search, versioning, and permissions into its basic objects, enabling applications to leverage framework services with little or no extra work.
ii Introduction to the WAF Developer Guide application This style indicates that the program is an end-user application (as opposed to system software). For example: Use Mozilla to browse the Web. [key] A key on the keyboard is shown in this style. For example: To use [Tab] completion, type in a character and then press the [Tab] key. Your terminal displays the list of files in the directory that start with that letter. [key]-[combination] A combination of keystrokes is represented in this way.
Introduction to the WAF Developer Guide iii $ # [stephen@maturin stephen]$ leopard login: user input Text that the user has to type, either on the command line, or into a text box on a GUI screen, is displayed in this style. In the following example, text is displayed in this style: To boot your system into the text based installation program, you must type in the text command at the boot: prompt.
iv Introduction to the WAF Developer Guide Warning Be careful to remove only the necessary Red Hat Applications partitions. Removing other partitions could result in data loss or a corrupted system environment. 3. Code Presentation Conventions In addition to the standard document conventions covered in Section 2 Document Conventions, there are some additional conventions related specifically to discussing source code: classname This is the name of a class in an object-oriented (OO) programming language.
Introduction to the WAF Developer Guide v PermissionDescriptor perm = new PermissionDescriptor(PrivilegeDescriptor.READ, acsObject, party); PermissionService.grantPermission(perm); first term The first occurrence of a term, such as the first time we introduce a bulletin-board and note its abbreviated form, bboard.
vi Introduction to the WAF Developer Guide
I. WAF Concepts This section covers the concepts of WAF. The intention is to provide both a very high-level architectural view and a closer review of the individual components. Table of Contents 1. WAF Overview ................................................................................................................................ 1 2. WAF Component: Persistence ....................................................................................................... 9 3. WAF Component: Kernel.........
Chapter 1. WAF Overview This chapter is an overview of the Web Application Framework architecture. This high-level viewpoint is especially useful for gaining a good understanding of how WAF works. It is written with both the technical developer and the technical manager/team leader in mind. WAF is a web application development framework. Some of the web applications that have been developed using WAF include Red Hat Content Management System and Red Hat Portal Server.
2 Chapter 1. WAF Overview 1.1. General Architecture The WAF architecture described in Figure 1-1 follows the standard n-tier design pattern, with separate presentation, domain (business logic), data, and data model layers. Web applications built on WAF also follow the same n-tier design patter, leveraging the infrastructure provided by WAF. 1.1.1.
Chapter 1. WAF Overview 3 1.1.1.1. Presentation Layer The Presentation Layer is responsible for presenting information to the end user. The presentation layer accepts processed, structured data from the domain layer and is responsible for styling the data appropriately and delivering the content in a format appropriate for the end user. 1.1.1.2.
4 Chapter 1. WAF Overview Figure 1-3. Basic Configuration 1.2.2. Services Services are building blocks that address generic requirements common to most WAF applications.
Chapter 1. WAF Overview 5 Figure 1-4. Basic Configuration As shown in Figure 1-4, all services follow the n-tier design pattern discussed in Section 1.1 General Architecture, providing: • A user interface for interacting with the Framework’s Domain Objects. • Domain logic. • The metadata (PDL) required for persistence of the Framework’s Domain Objects. • Data storage as appropriate. Services are discussed in more detail in Chapter 4 WAF Component: Services.
6 Chapter 1. WAF Overview 1.2.3. Infrastructure Infrastructure contains software to support the mechanics of application building at each layer of the architecture (for example, serving page requests, styling the user interface, logging, specifying metadata, storing data, etc.). Note This infrastructure exists independently from any specific problem domain and generally does not depend on other WAF systems. 1.2.4.
Chapter 1. WAF Overview 7 Figure 1-5. Basic Configuration Again, each application follows the same n-tier design pattern, and builds upon the kernel and services provided by WAF.
8 Chapter 1.
Chapter 2. WAF Component: Persistence This chapter discusses the persistence layer in the overall Web Application Framework. This was originally discussed in Section 1.2.4 Persistence. You can find persistence tutorials in Chapter 9 Persistence Tutorial. 2.1. Persistence Overview The storage and retrieval of persistent data is a common requirement of business applications. WAF provides a persistence layer as a generic solution to this requirement.
10 Chapter 2. WAF Component: Persistence For a listing of PDL terms, see Appendix D PDL Syntax. Examples of PDL usage can be found in Chapter 9 Persistence Tutorial. 2.4.
Chapter 3. WAF Component: Kernel This chapter discusses the kernel layer, which provides services used by several parts of the WAF system. This component was initially explained in Section 1.2.1 Kernel. This discussion focuses on the various parts of the kernel component which a developer will need in building for WAF. For information on how to utilize the kernel in WAF applications, see Chapter 10 Kernel Tutorial. 3.1. Users and Groups Applications have users.
12 • Chapter 3. WAF Component: Kernel What parties have a particular privilege on a particular object? • What privileges does a particular party have on a particular object? • On what objects does a particular party have a particular privilege? Much of the power of the permissions system comes from the flexibility of the model. The assertions described above are not the only input. Each privilege can imply some set of privileges.
Chapter 4. WAF Component: Services WAF provides a number of generic services that can be used in developing a WAF application. Services leverage the business logic provided by the kernel, and provide specialized domain APIs to solve specific, common business problems. Examples of services include notification, workflow, and versioning. Services were originally discussed in Section 1.2.2 Services. Implementation examples are covered in Chapter 11 Services Tutorials. 4.1.
14 • • Chapter 4. WAF Component: Services Entertainment • Music • Movies • Television Shows Education • • Literacy • Testing • Home Schooling • Theories Sports • Scores • Venues • Kinds of sports • Basketball • Chess • Soccer Example 4-1. Sample Taxonomy The categorization service provides: • The ability to set up multiple independent taxonomies. • A system for importing predefined taxonomies from XML files. • A user interface for managing categories.
Chapter 4. WAF Component: Services 15 introduced to the market. Rather than requiring programming for this straightforward functionality, WAF provides the formbuilder service. The formbuilder service enables non-technical users to build such applications via a web interface, without any programmer intervention. The formbuilder service provides a variety of widgets to choose from, such as a date widget which makes the part of the HTML form that allows you to specify a date.
16 Chapter 4. WAF Component: Services as reported by the user’s browser, and compares them to the list of languages and locales that the system is configured to support. For example, a user can configure the preferred first language to be German ("de") and the preferred second language to be British English ("en_GB"). If the system is configured to support both of the languages, then the globalization service will choose "de" as the preferred locale.
Chapter 4. WAF Component: Services 17 1. The message body should have a MIME type of text/plain or text/html. 2. Each attachment to the message can have an arbitrary MIME type and format. Messages can also refer to any ACSObject on the system. MessageParts represent a message part (an attachment) in the sense of a multipart MIME message. Each part has some content represented as an arbitrary block of bytes and a MIME type that identifies the format of the content.
18 Chapter 4. WAF Component: Services 4.10. Workflow Service Workflows allow specialized members of a group to collaborate using a standard process. Developers can define new workflows using the workflow service. A workflow contains a set of tasks, each of which may depend on one or more other tasks. A task is a single unit of work. It can be enabled, disabled, or finished. A task may be assigned to a user or group. Section 11.4.
Chapter 5. WAF Component: Presentation Information in the database ultimately needs to be presented to the user for viewing and manipulating. The Web Application Framework provides a variety of systems to display and style information. Each system has its strengths and weaknesses, and is designed for specific situations. The following sections discuss the various methods available for presentation under WAF. Each approach is designed to be complementary to the other approaches.
20 • Chapter 5. WAF Component: Presentation JavaServer Pages (JSP) is a J2EE standard for scripting dynamic web pages. JSPs typically produce HTML, though they can generate any XML markup. WAF supports the use of JSPs as a primary means of writing web UIs. 5.2. CSS and XSLT WAF uses two technologies together, CSS and XSLT, to add style to logical (i.e. all form, no style) markup. • CSS is used to control the style properties of HTML documents.
Chapter 5. WAF Component: Presentation 21 BasePresentationManager or swap it out with its own to use a different algorithm for choosing a stylesheet for the current request. XSLT integration into WAF is accomplished by associating stylesheet documents with both WAF packages and site nodes. A default stylesheet is associated with each WAF package, and the package defaults can be overridden within a particular URL prefix’s scope.
22 Chapter 5. WAF Component: Presentation com.arsdigita.templating.PatternStylesheetResolver class is the new preferred resolver and now the default setting. 5.2.2.2. Pattern Based Resolution The primary implementation of the stylesheet resolver interface is the class com.arsdigita.templating.PatternStylesheetResolver. The core idea behind this approach is that there is a list of abstract paths containing placeholders, of the form ::key::.
Chapter 5. WAF Component: Presentation 23 5.2.2.3. Pattern Generators The com.arsdigita.templating.PatternGenerator interface provides the mechanism for introducing new placeholders in the pattern based stylesheet resolver. This interface contains the single method: public String[] generateValues(String name, HttpServletRequest request); This method may use any available state information to generate a list of values for the placeholder.
24 Chapter 5. WAF Component: Presentation locale This pattern generator expands to the current kernel execution context locale. ie the value returned by a call to com.arsdigita.kernel.Kernel.getContext().getLocale();. outputtype If the outputType request parameter is set, this expands to one of text-plain, text-javascript, or text-html if the parameter value is text/plain, text/javascript or text/html respectively. In all other cases no values are generated.
Chapter 5. WAF Component: Presentation 25 5.3. JavaServer Pages (JSP) JSP technology is a J2EE standard for presentation. JSP pages have full access to WAF APIs. For documentation and tutorials on how to write JSPs, see http://java.sun.com/products/jsp/docs.html. The strength of JSP is the ease with which a developer can rapidly develop and modify a single page. JSPs can be recompiled on the fly, and changing layout is straightforward, since a JSP is patterned after a standard HTML file.
26 Chapter 5. WAF Component: Presentation Figure 5-1. Tabbed Pane Using Bebop Components 5.4.1. Working With Bebop Chapter 12 Presentation (Bebop) Tutorial discusses specifics of implementing Bebop. This section is a more abstract look into the design and usage of Bebop. Bebop components follow these guidelines: • Provide a tree of components; components are also containers. • Consolidate parameter validation logic and support for custom validators. • Lock data structures for reuse across requests.
Chapter 5. WAF Component: Presentation 27 TabbedPane tabs = new TabbedPane(); page.add(tabs); tabs.addTab("Browse", new BrowsePane()); tabs.addTab("Search", new SearchPane()); tabs.addTab("Roles", new RoleAdminPane()); page.lock(); Document doc = page.buildDocument(sreq, sresp); transform(sreq, sresp, doc); } Example 5-2. Basic Bebop Page 5.4.1.1. Bebop Lifecycles Bebop components used in a page design have a regular lifecycle. UI Lifecycle 1. Register each component. a.
28 Chapter 5. WAF Component: Presentation 5. Fire the control event. When a client sends a request to a Bebop page (e.g. a mouse-click on a tab); only one component receives the request. This triggers the respond method of that component only; no other components response methods are involved. If the request state is deemed valid, the selected component has the opportunity to respond, perhaps changing the request state and thus the outcome of further processing.
Chapter 5. WAF Component: Presentation 29 1. The requested JSP obtains an XML document from a Bebop page object and the current request state. 2. The tags in the requested JSP construct a new XML document, copying pieces from the Bebop page where needed. 3. The resulting XML is passed as the XML input to an XSLT transformation step, using global stylesheets. 4. The final result is sent to the user’s browser. 5.4.3.
30 Chapter 5. WAF Component: Presentation define:option label=" %= rtexpr % " value=" %= expr % "/ % } % /define:radioGroup This is especially true if the options in the group do not change frequently. Also, the contents of stateless components (Labels, Links, Images, etc.) may change between requests, though the position of the component in the page should not. Further examples can be found at Appendix A Bebop Tag Library Reference.
Chapter 6. WAF Component: Web The Web component of WAF makes the persistent data and domain logic of your application available to others over protocols such as HTTP. It integrates the Java Servlet API and the kernel and persistence components of WAF. For more information about building and deploying your own applications, see Chapter 8 WAF Application Development Tutorial. 6.1. Applications WAF supports deploying multiple instances of an application type.
32 • Chapter 6. WAF Component: Web Request context — The BaseServlet packages certain facts about the request, such as the current user, current application, etc., and loads them into a context object whose data is available to any code running inside the servlet. The BaseServlet is used in two principal ways in WAF: • New applications will sometimes use custom servlets that extend the BaseServlet and use the Servlet API to implement their UI.
II. Equipping Developers This section provides information needed to develop on the Web Application Framework. Included is a chapter on setting up a development environment and a number of tutorials related to the WAF components. Table of Contents 7. Developing with WAF ................................................................................................................... 35 8. WAF Application Development Tutorial..................................................................................
Chapter 7. Developing with WAF This chapter introduces prerequisites to working with WAF as well as the tools that are available to the developer. It is presumed that anyone developing for WAF has sufficient knowledge in his or her area of expertise and can perform accordingly. For this reason, both high-level concepts, such as database design and programming theory, and basic concepts, such as command line usage and shell scripting, are not addressed. 7.1.
36 Chapter 7. Developing with WAF includes a secure telnet replacement and a secure file-copy utility (scp). Both open source and proprietary implementations of SSH exist for virtually every OS. An open source implementation of SSH for Unix-like systems can be found at http://www.openssh.org. The site also provides pointers for various alternatives to OpenSSH.
Chapter 7. Developing with WAF 37 It is possible to have Developer Support enabled and not running. This still provides a measurable performance hit because it registers a listener. Once enabled in enterprise.init and recognized by the servlet container, you can turn Developer Support on and off via the administrator page. See Section 7.3.2.2 Developer Support Features for more information on controlling Developer Support via the administrator page. 7.3.2. Using Developer Support 7.3.2.1.
38 Chapter 7. Developing with WAF Show Hits To Developer Support The Show hits to developer support link on the Developer Support index page toggles the tracking of requests against the Developer Support application itself. As most developers will not be interested in this information, this information is usually left hidden. 7.4.
Chapter 7. Developing with WAF 39 Tip It is recommended to only select Warning for the options: • Methods overridden but not package visible • Methods with a constructor name • Hidden catch blocks Otherwise, your compilations will output hundreds of messages in your Tasks window. 7.4.3. Eclipse Project Configuration This section covers adding and configuring projects to Eclipse for developing with WAF. 7.4.3.1. Adding Projects WAF 1. In Eclipse, go to File = 2. Select Java Project.
40 Chapter 7. Developing with WAF CMS and Other Projects To add CMS or other projects to Eclipse, follow the same steps as for adding WAF, with the following exceptions: • For CMS and other projects that depend on WAF, you do not need to add external JARs. Eclipse will pick up these libraries from WAF. • When you reach the Java Settings dialog box, select the Projects tab. For CMS, add a dependency upon WAF. For projects that use both WAF and CMS, add a dependency upon both WAF and CMS.
Chapter 7. Developing with WAF 41 #clean cd $CCM_HOME ant clean #build cd $CCM_HOME ant deploy #javadoc cd $CCM_HOME ant javadoc ant deploy-api Example 7-1. Shell-script to build and deploy from within Eclipse In your ant.properties file, make sure that you have set compile.debug=on. Once you have created this shell script, in Eclipse go to Run = External Tools = Configure. A dialog box will appear for configuring external tools.
42 Chapter 7. Developing with WAF WAF Debugging Setup In Eclipse, select Run = Debug. In the dialog box, select Remote Java Application and then click on New. Then enter information for the following tabs: • Connect Tab Enter a name for your debug configuration, such as webapp-debug. Select the same project previously selected as the main project. Enter the host name (localhost should be fine). Use 8000 as your connection port. Also, select Allow termination of remote VM.
Chapter 7. Developing with WAF 43 7.5.1. Log4J Using System.out.println() for logging has numerous drawbacks. 1. It degrades performance. There is no easy way to conditionally turn off logging statements system-wide, without resorting to error-prone and maintenance-intensive workarounds such as: if (DEBUG) { System.err.println("foo=" + foo); } where DEBUG is a system-wide boolean parameter that you can easily set to true or false. 2.
44 Chapter 7. Developing with WAF greater than or equal to the current level of the logger. For example, if you set the foo.bar.baz logger’s level to warn, it will only log message whose level is warn, error, or fatal. By setting the level for the foo.bar logger, you are automatically setting it for foo.bar.baz and foo.bar.quux, unless overridden on a more specific level. 6. Miscellaneous other items such as filters2 and flexible configuration.
Chapter 7. Developing with WAF 45 try { doSomething(); } catch (FooException ex) { s_log.debug(ex); } Example 7-4. Swallowed stack trace anti-pattern There is no debug(Throwable throwable) method — there is only debug(Object msg). Same goes for warn, info, etc. The above statement is essentially equivalent to the following: s_log.debug(String.valueOf(ex)); This is probably not the intended result.
46 Chapter 7. Developing with WAF package foo.bar; import org.apache.log4j.Logger; public class Baz { private final static Logger s_log = Logger.getLogger(Baz.class); } Example 7-7. Standard way of instantiating loggers This instantiates a singleton logger named foo.bar.Baz. Note, however, that assigning the logger to a private static variable is merely a matter of syntactic convenience. If you ever need to, you can obtain the same logger from anywhere else in your code. package foo.frob; import org.
Chapter 7. Developing with WAF 47 } Example 7-9. Adjusting logging level temporarily In the above example, you end up enabling the PreparedStatement logger only for the duration of the doSomething() method. This may significantly cut down the number of queries logged, thus making it easier for you to see what is going on. The obvious caveat here is that extraneous queries may still end up getting logged in the presence of multiple threads.
48 Chapter 7. Developing with WAF What kind of I/O does log4j use: buffered or unbuffered? It depends on how the particular appender is configured. In the case of FileAppender 13, you have the option of configuring it either way. See the methods getBufferedIO() 14 and setBufferedIO(boolean) 15. For maximum performance, you should use buffered I/O. Only switch to unbuffered I/O when you suspect logged information may be getting lost. By default, the file appender is buffered. 7.5.6.
Chapter 8. WAF Application Development Tutorial This tutorial introduces the concepts and techniques needed to create your own application in the Red Hat Web Application Framework. It describes how to define the application so that it can be packaged and installed wherever the WAF platform is installed. 8.1. Terms and Assumptions In addition to the the assumptions in Section 1 Assumptions About WAF Developers (e.g.
50 Chapter 8. WAF Application Development Tutorial The standard layout of files in the build environment of a WAF application follows the guidelines set out in the Tomcat Application Developer’s Guide (http://jakarta.apache.org/tomcat/tomcat-4.1doc/appdev/index.html), which are based on the Servlet specification. binder/ The name of the directory you choose to hold your application source and build files. Our example application is a note binder, so we call our directory binder.
Chapter 8. WAF Application Development Tutorial 51 8.5. Modeling Your Application A new application is represented by a persistent Application object. A typical application uses the WAF persistence layer to store and query the state of an Application data object. An Application is more than just storage, however. It is also a set of behaviors, principally having to do with containment and dispatch. Refer to Section 6.1 Applications to learn more.
52 Chapter 8. WAF Application Development Tutorial 8.7. Java Domain Objects Each object type will have a corresponding domain class written in Java. The domain class exists to provide a Java-native API to your object type’s data and to behaviors that are specific to your application. Refer to Chapter 2 WAF Component: Persistence. package com.example.binder; import import import import import com.arsdigita.persistence.DataCollection; com.arsdigita.persistence.DataObject; com.arsdigita.util.Assert; com.
Chapter 8. WAF Application Development Tutorial 53 /** * Removes note
from the set of notes tracked by this * Binder
. * * @param note The Note
to remove */ public void removeNote(final Note note) { Assert.exists(note, Note.class); if (s_log.isDebugEnabled()) { s_log.debug("Removing note " + note + " from " + this); } remove(NOTES, note); } public final String getContextPath() { return "/binder"; } } Example 8-3. binder/src/com/example/binder/Binder.
54 Chapter 8. WAF Application Development Tutorial package com.example.binder; import import import import import import import import import import com.arsdigita.db.Sequences; com.arsdigita.domain.DomainObject; com.arsdigita.persistence.DataObject; com.arsdigita.util.Assert; com.arsdigita.util.UncheckedWrapperException; java.io.IOException; java.io.Writer; java.sql.SQLException; java.math.BigInteger; org.apache.log4j.Logger; /** * A bit of text with a title. * * @see com.example.binder.
Chapter 8. WAF Application Development Tutorial 55 /** * Sets the title of the note. * * @param title The String
title; it cannot be null */ public final void setTitle(final String title) { if (s_log.isDebugEnabled()) { s_log.debug("Setting title of " + this + " to " + title); } Assert.exists(title, String.class); set(TITLE, title); } /** * Gets the body of the note.
56 Chapter 8. WAF Application Development Tutorial In many ways, Note.java is just like Binder.java. It declares its data object type; it defines an instantiator; and it adds some plumbing for getting the runtime data object type and for constructing a wrapper DomainObject. Since the properties of the data object type differ from those of Binder, the Java accessors and mutators are different. Of more interest is the fact that Note has additional business logic for outputting the Note object as XML.
Chapter 8. WAF Application Development Tutorial 57 * * @return The String
title; it cannot be null */ public final String getTitle() { return (String) get(Note.TITLE); } /** * Gets the body of the current note. * * @return The String
body; it may be null */ public final String getBody() { return (String) get(Note.BODY); } } Example 8-6. binder/src/com/example/binder/NoteCollection.java 8.8.
58 Chapter 8. WAF Application Development Tutorial e.getFactory().registerInstantiator (Binder.BASE_DATA_OBJECT_TYPE, new DomainObjectInstantiator() { protected final DomainObject doNewInstance (final DataObject data) { return new Binder(data); } }); } } Example 8-7. binder/src/com/example/binder/Initializer In the section above, you defined object-relational mapping metadata. The PDLInitializer serves to load that metadata into the runtime.
Chapter 8. WAF Application Development Tutorial 59 /** * A servlet to serve pages of the binder application. * * @see com.example.binder.Binder * @author Justin Ross */ public final class BinderServlet extends BaseApplicationServlet { private static final Logger s_log = Logger.getLogger(BinderServlet.class); public void doService(final HttpServletRequest sreq, final HttpServletResponse sresp, final Application app) throws ServletException, IOException { sresp.getWriter().write("YO"); } } Example 8-9.
60 Chapter 8. WAF Application Development Tutorial 8.10. Integrating Your Package With CCM Tools The WAF environment includes a set of tools for loading and configuring software such as our example application. In order to integrate with those tools, your application needs to supply some information in the right places. The CCM package loader, part of the ccm command line tool, takes a package and loads its initial configuration, schema, and data.
Chapter 8. WAF Application Development Tutorial 61 Creating A ccm Tool Upgrade Script in Java 1. Create arbitrary Java code that is accessible via a static main() method. ## file called com/arsdigita/cms/AnUpgrade.java package com.arsdigita.cms; public class AnUpgrade { public static void main(String[] args) { System.out.println("Sample upgrade running"); } } 2. Create or edit the upgrade spec file at ${package_name}.upgrade on the classpath. The file described looks like this: ## file called ccm-cms.
62 Chapter 8.
Chapter 9. Persistence Tutorial This chapter presents a tutorial for using the persistence system. It presumes you are familiar with the concepts covered in Chapter 2 WAF Component: Persistence. This tutorial will first discuss using Data Objects, which is followed by a tutorial on using the features of PDL. Referenced throughout this tutorial is the Persistence Glossary. 9.1.
64 • Chapter 9. Persistence Tutorial A database schema to handle the storage of the Data Objects. Some designers may feel more comfortable starting with a database design and then designing the objects that use the schema, or vice versa. You may begin with either one, but both should be designed as part of the persistence layer. The UML model is typically designed for the Data Objects using a UML modeling tool.
Chapter 9. Persistence Tutorial 65 9.2.3.2. Model and Object Type When creating a PDL file, the first line of the file must be the name of the model that defines the namespace for the block definitions in the PDL file. This is similar to the name of the package in a Java class and is necessary to avoid name collisions of object types that have the same name and which exist in different PDL files. This example will use the tutorial model.
66 Chapter 9. Persistence Tutorial This object type definition almost models the SQL that we have defined above but it is missing any mention of the unique constraint. To allow for this and to allow the DDL generator to correctly generate unique constraints the persistence layer allows you to specify either a single property or a set of properties as being unique. The syntax for this is as follows: SINGLE UNIQUE PROPERTY: object type User { BigDecimal id = users.id INTEGER; unique String[0..
Chapter 9. Persistence Tutorial • SessionManager Manager.html. 67 — http://rhea.redhat.com/doc/waf/6.0/api/com/arsdigita/persistence/\ Session- This class is responsible for initializing the Session class. Users of persistence will use it to get a pointer to the Session class through SessionManager.getSession() (http://rhea.redhat.com/doc/waf/6.0/api/com/arsdigita/persistence/\ SessionManager.html#getSession()). • Session — http://rhea.redhat.com/doc/waf/6.0/api/com/arsdigita/persistence/Session.
68 Chapter 9. Persistence Tutorial • PersistenceException PersistenceException.html — http://rhea.redhat.com/doc/waf/6.0/api/com/arsdigita/persistence/\ This is an unchecked exception that is thrown whenever there is an error within the persistence system. It typically contains an error message as well as some debugging information. 9.2.5.
Chapter 9. Persistence Tutorial 69 DataCollections are also useful when it is necessary to perform certain tasks on a large number of Data Objects. Suppose you want to add the publications to a java.util.Collection that can then be passed around. The following code will create the java.util.Collection (remove the "\" and make it all one line): DataCollection publication = SessionManager.getSession().retrieve\ ("tutorial.Publication"); Collection collection = new ArrayList(); pub.addFilter(pub.
70 Chapter 9. Persistence Tutorial // a "reference key" declaration and "id" is not defined // as one of the attributes reference key (magazines.magazine_id); } 9.3. Associations So far, the documentation has discussed how to create simple Data Objects to access the database. These features, while sufficient to build many systems, lack the ability to relate object types to each other. To address these needs, the persistence system contains the concept of associations.
Chapter 9. Persistence Tutorial 71 Note The order of the join path is important. The information that the developer has must come first. That is, when the developer needs to retreive articles, the information he has is the Magazine (he needs to know for which magazine to get the articles). Therefore, the first line of the join path specifies how to join the magzines table to the mapping table.
72 Chapter 9. Persistence Tutorial } 9.3.3. Role References Developers ofen only need to be able to obtain associated information in a single direction. For instance, if authors have screen names that are used and can be shared, it is useful to be able to look up the screen name for a given author. However, it may not be as important to look up the author that corresponds to a given screen name. In this case, developers should use a Role Reference.
Chapter 9. Persistence Tutorial 73 // create the mapping table with the page_number column. BigDecimal pageNumber = magazine_article_map.page_number INTEGER; } For more information about Link Attributes and their use, see Section 9.8 Link Attributes. 9.3.5. Using Java to Access Associations Now that you have seen how to declare associations within PDL, you can learn the different ways to access the information from Java.
74 Chapter 9. Persistence Tutorial association has a Multiplicity of 0..1 or 1..1 (or any upper bound = 1) then developers should use the setAssociation(String attr, DomainObject dobj) method. 9.3.5.2. Using Role References Role References can be treated in exactly the same way as standard associations. The only practical difference between Role References and standard associations is that Role References are one-way associations and standard associations are two-way associations.
Chapter 9. Persistence Tutorial 75 To accomplish the task of retrieving the paragraphs as mentioned above, you could declare the following DataQuery in your PDL file: model tutorial; // the first line indicates that it is a query and the name of the query query paragraphMagazines { // the next section maps the attributes to the java type so that the // same type is returned regardless of which database driver is used.
76 Chapter 9. Persistence Tutorial The PDL is simply a Data Query with extra Attribute definitions (remove the "\" and make it all one line). query MagazineToAuthorMapping { // the next two lines are declaring that objects will be returned Magazine magazine; Author author; do { select publications.name, issue_number, publication_id, authors.first_name, authors.last_name, author_id from magazines, publications, articles, authors, magazine_article_map, article_author_map where publications.
Chapter 9. Persistence Tutorial 77 9.4.2.1. Executing Arbitrary DML Data Operations are similiar to DataQueries in both structure and use. However, while they are retrieved in a fashion similar to DataQueries, they are executed differently. After the query is retrieved, the program can set bind variables, after which it is executed. Suppose you want to create a magazine with ID 4 using all articles in the system that are not yet currently in a magazine.
78 Chapter 9. Persistence Tutorial select nvl(max(magazine_id), 0) + 1, :title from magazine_id; end; / show errors To do this, first include the above statement in your SQL file, so that it will be defined in the database when your package is installed.
Chapter 9. Persistence Tutorial 79 Note The do call and OUT parameters are not available for Postgres because Postgres has not yet implemented CallableStatements or OUT parameters. 9.4.2.2.2. PL/SQL Functions Retrieving a single value back from a function is almost identical to using OUT parameters for procedures. First, declare your PL/SQL in your SQL file.
80 Chapter 9. Persistence Tutorial 9.5.1. Filtering 9.5.1.1. Overview The filtering system is complex, in that it allows developers to create complex expressions by combining filters, yet simple in that it provides convenience methods for developers with simple needs. It is important to realize that by default, Filters simply filter the resulting data from the query.
Chapter 9. Persistence Tutorial 81 } To retrieve all users whose last name is "Smith", do the following: DataQuery query = session.retrieveQuery("UsersGroups"); query.addEqualsFilter("lastName", "Smith") while (query.next()) { System.out.println("First name = " + query.get("firstName") + "; Last name = " + query.get("lastName") + "; Group = " + query.get("groupName")); } To get all users whose last name starts with "S", use the addFilter method: DataQuery query = session.
82 Chapter 9. Persistence Tutorial System.out.println("First name = " + query.get("firstName") + "; Last name = " + query.get("lastName") + "; Group = " + query.get("groupName")); } Now suppose you want to get all users with a last name that is the same as the variable lName or is Smith, and with a first name that matches the variable fName or is John. You could do this as follows: DataQuery query = session.retrieveQuery("UsersGroups"); FilterFactory factory = query.
Chapter 9. Persistence Tutorial 83 query retrieveAllUsers { User myUser; do { select user_id, first_name, last_name from users } map { user.id = users.user_id; user.firstName = users.first_name; user.lastName = users.last_name; } } DataQuery query = session.retrieveQuery("retrieveAllUsers"); // notice how the attribute name corresponds directly to what // in the map" section of the query and is actully // the object type . attribute name query.addEqualsFilter("user.
84 Chapter 9. Persistence Tutorial select article_id from authors, author_article_map where authors.author_id = author_article_map.author_id and lower(last_name) like :lastName || ’%’ } map { authorID = authors.author_id; } } query retrieveSelectedArticles { BigDecimal articleID; String title; do { select article_id, title from articles } map { articleID = articles.article_id; title = articles.
Chapter 9. Persistence Tutorial 85 9.5.1.6. Filtering Using Functions The filtering methods discussed so far work well when the developer only needs to filter directly off of columns or values. However, they do not work well if the developer wants a case-insensitive filter or needs to use some other function to manipulate the data in the columns.
86 Chapter 9. Persistence Tutorial String firstName; String lastName; String groupName; do{ select * from users, groups, membership where users.user_id = membership.member_id and membership.group_id \ = groups.group_id } map { firstName=users.first_name; lastName=users.last_name; groupName=groups.group_name; } } You can order this query by the user’s last name, as follows: DataQuery query = session.retrieveQuery("UsersGroups"); query.addOrder("lastName asc"); while (query.next()) { System.out.
Chapter 9. Persistence Tutorial 87 from (select level l, related_category_id from (select related_category_id, category_id from cat_category_category_map where relation_type = :relationType) connect by prior related_category_id = category_id start with category_id = :categoryID) m, cat_categories c where c.category_id = m.related_category_id } map { level = m.l; categoryID = c.category_id; name = c.name; description = c.description; isEnabled = c.
88 Chapter 9. Persistence Tutorial 9.5.4. How it Works You may be wondering how to add filters and bind variables after retrieving the query. The persistence system does not actually execute the required SQL query until the first call to next(). Therefore, the DataQuery (or DataCollection or DataAssociationCursor) can be passed around and have Filters and ORDER BY statements added and variables bound before the first element is retrieved.
Chapter 9. Persistence Tutorial 89 9.6.3. Incorrect Attribute Mappings When you receive an error indicating that an attribute cannot be found or an attribute name is invalid, make sure that there are no typos. Simple typos cannot be caught by the compiler and cause many errors. 9.6.4. Problems with Transactions Typically, two types of mistakes occur when dealing with Transactions within the WAF. The first type of mistake is trying to access Data Objects (and hence the database) outside of a transaction.
90 Chapter 9. Persistence Tutorial Data Model Problem Likely Cause in PDL Missing foreign keys Object Model doesn’t use associations, but data model does, e.g. Message object has BigDecimal authorID property instead of User author property, in these cases an easy fix is to create the author association without removing the authorID attribute so that existing java code continues to work. Missing not null The property in PDL is [0..1] when it should be [1..1].
Chapter 9. Persistence Tutorial 91 9.7.2. Manual Transaction Management When you manage your transactions locally, your starting point is the SessionManager class. From there, you can get access to the Session class and then to the Transaction. Specifically, the Session provides access to the TransactionContext, which has methods for beginning a transaction, committing a transaction, and aborting a transaction.
92 Chapter 9. Persistence Tutorial 9.8. Link Attributes When modeling many real-world problems, it is common to encounter situations that require associating data with a link between two objects. This document describes how to model these situations using the persistence system. 9.8.1. Introduction When creating a model for use in a particular domain, there are usually cases in which a simple association between two object types will not suffice.
Chapter 9. Persistence Tutorial String[1..1] // ... 93 name = groups.name; } object type User { BigInteger[1..1] id = users.user_id; String[1..1] name = users.name; String[1..1] email = users.email; // ... } association { Group[0..n] groups = join users.user_id to \ group_member_map.member_id, join group_member_map.group_id to \ groups.group_id; User[0..n] members = join groups.group_id to \ group_member_map.group_id, join group_member_map.member_id to \ users.user_id; Date[1..
94 Chapter 9. Persistence Tutorial 9.8.2.1. Creating and Initializing Links The DataAssociation.add(DataObject object) method is used when creating a link between two objects. If the association was defined with the association keyword, this method returns the link DataObject. The link DataObject can then be used to set any link attributes that have been defined. This is illustrated in the following example: // Put the current session in a variable for convenience. Session ssn = SessionManager.
Chapter 9. Persistence Tutorial 95 System.out.println(email + ": " + membershipDate); } 9.8.2.3. Updating Link Attributes The DataAssociationCursor.getLink() method can be used to fetch the DataObject that represents a link. This DataObject can then be used to modify the link and persist the results. // Put the current session in a variable for convenience. Session ssn = SessionManager.getSession(); // Get a group. DataObject group = ssn.retrieve(new OID("Group", ...)); // Get the members association.
96 Chapter 9. Persistence Tutorial // Get the members association. DataAssociation members = (DataAssociation) group.get("members"); // Iterate over new members of the group. DataAssociationCursor cursor = members.getDataAssociationCursor(); // Get yesterday’s date. java.util.Date yesterday = new java.util.Date(System.currentTimeMilis() - \ 1000 * 60 * 60 * 24); // Restrict to recent members Filter f = cursor.addFilter("link.membershipDate f.
Chapter 9. Persistence Tutorial 97 3. Generates the events for storing information in the schema in memory so that the ObjectType returned can be used within DataObjects. 4. Executes the DDL to create the schema. Unfortunately, this causes the system to commit the current transaction, so it is important that you do not do anything else in the same transaction as saving the DynamicObjectType. 5. Assuming the DDL executed successfully, saves the PDL that was generated to a table within the database.
98 Chapter 9. Persistence Tutorial In addition, there are numerous examples that one could concoct where an EJB server (at least initially) could be deemed to be an overkill given the problem being tackled. This is the sledgehammer-to-crack-a-nut problem. In essence, the J2EE specification does not mandate a 2, 3, or multitier application model, nor realistically could it do so. The point is that it is important to use appropriate tools for a given problem space.
Chapter 9. Persistence Tutorial 99 2. Developer Specific Questions 2.1. I am new to the persistence API. Where do I start? Start with this book’s table of contents which lists all available persistence documents. In addition, the Section 10.2 Domain Objects Tutorial introduces the API used by the WAF and provides you with code samples for implementing your own Domain Objects. Additionally, you may want to consult the document on Section 9.2 Beginning With Data Objects, as well as material on Section 9.
100 Chapter 9. Persistence Tutorial Suppose you are defining an object type named "ChewingGum" that extends ACSObject. To specify that an object type extends another, do the following: • Before defining your object type but after declaring your model, make sure to import the ACSObject type. • When defining your object type, specify that the supertype of the "Chewing Gum" object type is "ACSObject" and import the namespace for ACSObject. In the PDL file, you currently write: import com.arsdigita.kernel.
Chapter 9. Persistence Tutorial 101 2.9. How do I retrieve a DataQuery, DataCollection, or DataAssociation in the order I want (e.g., there is a sort_order link attribute that I want to sort by)? To do this, use the setOrder() method. For further information, see Section 9.5.2 Ordering. 2.10. What is the OID class used for? The OID class encapsulates the details of the primary key of an object. You use instances of OID for retrieving an object from the database and you set the OID to a known value.
102 Chapter 9.
Chapter 10. Kernel Tutorial This chapter discusses how to use permissioning and domain objects, answers frequently-asked questions about the WAF security mechanisms and API, and touches on extending the authentication system. Please refer to Chapter 3 WAF Component: Kernel for more information about the concepts used in these tutorials. 10.1. Permissions Tutorial 10.1.1. Granting Access Granting access to a party is accomplished by creating a PermissionDescriptor and passing it to PermissionService.
104 Chapter 10. Kernel Tutorial 10.1.2. Revoking Access Revoking a privilege on an object from a party is accomplished by creating a PermissionDescriptor and passing it to PermissionService.revokePermission. The following example revokes read privilege on MyACSObject 50 from Group 5: import import import import com.arsdigita.kernel.permissions.PermissionService; com.arsdigita.kernel.permissions.PermissionDescriptor; com.arsdigita.kernel.permissions.PrivilegeDescriptor; com.arsdigita.persistence.
Chapter 10. Kernel Tutorial 105 OID party = new OID("com.arsdigita.kernel.User", new BigDecimal(100)); PermissionDescriptor perm = new PermissionDescriptor(PrivilegeDescriptor.READ, acsObject, party); if (PermissionService.checkPermission(perm)) { // user 100 has read access on object 50 } else { // user 100 does NOT have read access on object 50. // You might handle this case by displaying an // "access forbidden" message.
106 Chapter 10. Kernel Tutorial DataCollection objects = SessionManager.getSession() .retrieve("example.MyACSObject"); OID party = new OID("com.arsdigita.kernel.User", new BigDecimal(100)); PermissionService.filterObjects(object, PrivilegeDescriptor.READ, party); // iterate over filtered results while (objects.next()) { ... } 10.1.5. Setting the Access Control Context of an ACSObject An ACSObject can inherit permissions from another ACSObject, which is called its context.
Chapter 10. Kernel Tutorial 107 10.2.1.1. Starting the Class A domain object starts off as a standard Java class that extends one of the four domain object base classes. Because of the auditing and permissioning requirements, AuditedACSObject is the base class. The code in Example 10-1 illustrates the initial definition of the class and the import statements. package com.arsdigita.notes; import import import import import import import import import import com.arsdigita.db.Sequences; com.arsdigita.
108 Chapter 10. Kernel Tutorial */ protected String getBaseDataObjectType() { return BASE_DATA_OBJECT_TYPE; } (4) } (1) The AuditedACSObject is imported from the auditing service package. (2) Log4j is used as a uniform debugging tool across WAF. The convention for initializing the log4j system is to initialize a category with the name of the current class, as shown here. (3) By extending this baseclass, this Java class becomes a domain object.
Chapter 10. Kernel Tutorial 109 public class Note extends AuditedACSObject { /** * BASE_DATA_OBJECT_TYPE represents the full objectType name for the * Note class **/ private static final String BASE_DATA_OBJECT_TYPE = "com.arsdigita.notes.Note"; /** * Default constructor. The contained DataObject is * initialized with a new DataObject with an * ObjectType of "Note". * * @see AuditedACSObject#AuditedACSObject(String) * @see com.arsdigita.persistence.DataObject * @see com.arsdigita.persistence.metadata.
110 Chapter 10. Kernel Tutorial * DataObject. * * @see AuditedACSObject#AuditedACSObject(OID) * @see com.arsdigita.persistence.DataObject * @see com.arsdigita.persistence.OID * * @exception DataObjectNotFoundException Thrown if we cannot * retrieve a data object for the specified OID * **/ (4) public Note(OID oid) throws DataObjectNotFoundException { super(oid); } ... } (1) The empty constructor is used to construct a new Note with its default data object type.
Chapter 10. Kernel Tutorial * new instance. * * @see com.arsdigita.persistence.Session#create(String) * @see com.arsdigita.persistence.DataObject * @see com.arsdigita.persistence.metadata.ObjectType **/ public DomainObject(String typeName) { Session s = SessionManager.getSession(); (1) if (s == null) { throw new RuntimeException("Could not retrieve a session from " + "the session manager while instantiating " + "a class with ObjectType = " + typeName); } m_dataObject = s.
112 Chapter 10. Kernel Tutorial } m_dataObject = s.retrieve(oid); if (m_dataObject == null) { throw new DataObjectNotFoundException ("Could not retrieve a DataObject with " + "OID = " + oid.toString()); } initialize(); } /** * Constructor. Creates a new DomainObject instance to encapsulate * a given data object. * * @param dataObject The data object to encapsulate in the new domain * object. * @see com.arsdigita.persistence.
Chapter 10. Kernel Tutorial 113 } ... } (1) These constructors are used to create an empty data object. The Session class is used look up the metadata that defines the object type and returns the data object. Because the data object is a private member variable, it is denoted m_dataObject. (2) These two constructors are used to create a DomainObject when a data object already exists. The OID-based constructor searches for a data object with the given OID.
114 Chapter 10. Kernel Tutorial 10.2.1.4. DomainObject Methods Several methods are inherited from DomainObject that are useful for building other methods for subclasses. In general, methods are added to subclasses to provide application logic specific to a business domain. A common case of this are accessors and mutators for data object properties. Such methods are usually called by process objects or UI components. 10.2.1.4.1.
Chapter 10. Kernel Tutorial 115 * Called from base class constructors (DomainObject constructors). */ protected void initialize() { super.initialize(); if (isNew()) { String typeName = getObjectType().getModel().getName() + "." + getObjectType().getName(); set("objectType", typeName); } } 10.2.2. Conventions 1.
116 Chapter 10. Kernel Tutorial Note that the Message class only support bodies with a primary MIME type of text. The MessageType interface defines legal MIME types for use in specifying a message body. 10.2.3.2. Referring to Other Objects Messages can store an optional reference to an ACSObject. This is useful for attaching messages to other items in the system. For example, a discussion forum might implement a Topic class to represent a collection of related messages.
Chapter 10. Kernel Tutorial 117 Note that the above two examples do not explicitly set a MIME type for the content. This is determined automatically when the content is handed off to a DataHandler for processing. In both cases shown above the content will be of type image/gif. 10.2.3.4. Simple Threading A Message object can store a reference to another Message object to implement basic threading.
118 Chapter 10. Kernel Tutorial msg[1].setRefersTo(anchor); msg[2] msg[3] msg[4] msg[5] msg[6] = = = = = msg[0].replyTo(from,body); msg[0].replyTo(from,body); msg[2].replyTo(from,body); msg[2].replyTo(from,body); msg[3].replyTo(from,body); Note that the two root-level messages (0 and 1) explicitly set the reference to our anchor object, but the replies do not.
Chapter 10. Kernel Tutorial 119 KernelHelper.getCurrentUser(request) is a convenience method that returns the current User object if available, returns null if the user is not logged in, or throws a RuntimeException if the user is logged in but does not exist. 4. How can a user be logged in but not exist in the database? WAF logs in users automatically using cookies (or other mechanisms), so it is possible for a client to present a valid cookie for a user that doesn’t exist.
120 Chapter 10. Kernel Tutorial 8. How do I log in a user using a username and password? KernelHelper.getKernelRequestContext(request).getUserContext().login(username, password, forever), where forever should be true if the user should be granted a permanent login cookie, false if the cookie should only last for the current session. For an example, see com.arsdigita.ui.login.UserRegistrationForm.
Chapter 10. Kernel Tutorial 121 12. How do I set a user’s password? Given the user’s UserAuthentication object, auth.setPassword(password) sets the user’s password. Caution Use the method auth.setPassword(password) carefully, as it can allow an attacker to override the user’s password. com.arsdigita.ui.login.PasswordValidationListener is a ParameterValidationListener that checks whether a form parameter value is a strong password. This listener should always be used before setting the user’s password.
122 Chapter 10. Kernel Tutorial 10.4. Extending the Authentication System The main strength of PAM is that the system may be extended with new authentication technologies. This is done by implementing new LoginModules. For example, an LDAP server can be used to authenticate a user by username and password. To integrate this behavior into WAF, a new login module (for example, LDAPLoginModule) must be defined. LDAPLoginModule would replace LocalLoginModule in the login configuration.
Chapter 11. Services Tutorials These tutorials build upon the discussion in Chapter 4 WAF Component: Services. 11.1. Categorization Tutorial This section presents an overview of using the categorization layer within WAF. The focus in this section is on describing the main concepts, classes, and interfaces that a programmer uses to work with the categorization API. 11.1.1. Overview This section partially overlaps with and elaborates on the material presented http://rhea.redhat.
124 Chapter 11. Services Tutorials Figure 11-1. Categorization scheme In the above diagram, the default parent of Televised Events is TV Shows. Its secondary parent is Sports. Once we have a categorization scheme, we can start categorizing objects. The following diagram shows three objects placed in the above categorization scheme. Figure 11-2. Categorized objects Note that an object can be categorized under more than one category. In the above example, the movie The Natural belongs to two categories.
Chapter 11. Services Tutorials 125 An application may own more than one category root. Each association may be labeled with a unique string, called a context. Figure 11-3. Categorization contexts In the above example, the application name is Content Section. It has two categorization schemes identified by the contexts legal and marketing, respectively. 11.1.2. Classes To use the categorization layer, you should become familiar with the following classes. com.arsdigita.categorization.
126 Chapter 11. Services Tutorials • removeChild(ACSObject object) is used to delete a mapping with a child. http://rhea.redhat.com/documentation/api/ccm-core6.1.0/com/arsdigita/categorization/Category.html\ #removeChild(com.arsdigita.kernel.ACSObject) • getChildren() returns all subcategories for the given category. See CategoryCollection. http://rhea.redhat.com/documentation/api/ccm-core6.1.0/com/arsdigita/categorization/Category.
Chapter 11. Services Tutorials 127 Category movies = new Category("Movies", "long television shows"); // Create the Romantic Comedies category under the Movies category. Category romance = new Category("Romantic Comedies", "Comedies with love stories"); romance.setDefaultParentCategory(movies); // Create the Titanic category under the Romantic Comedies category. Category titanic = new Category("Titanic", "A category for large movies"); // set this as the default category titanic.
128 Chapter 11. Services Tutorials Figure 11-4. Category hierarchy before and after code execution Adding the "Titanic" category to two categories looks the same as adding it to a single category. The first category to which an item (category or other ACSObject) is added is explicitly added as the default category. If a category/item only has one parent, that parent is implicitly the default. 11.2.2.
Chapter 11. Services Tutorials 129 Figure 11-5. Category hierarchy before and after deleting a category You can delete a category in the following ways: • Deleting Leaf Category — If the category is a leaf, call Category.delete(). • Remapping Child Categories — If you want to remove the current category and make all categories that were pointing to it point to its default parent (as in the example), call Category.deleteCategoryAndRemap().
130 Chapter 11. Services Tutorials Category b = new Category(new OID(124)); Category c = new Category(new OID(125)); if ( a != null && b != null && c != null ) { // Makes Category B the parent of Category A b.addChild(a); // Makes Category C the parent of Category A c.addChild(a); // Sets Category B as the default parent of Category A a.setDefaultParentCategory(b); } Example 11-3. Adding more parent categories Figure 11-6. Category hierarchy before and after adding parent categories. 11.2.4.
Chapter 11. Services Tutorials Figure 11-7. Category Hierarchy before and after removing parent categories 11.2.5. Retrieving all Subcategories of a given Category // assume that we have the Category OID (call it categoryOID) Category category = new Category(categoryOID); // subcategories is a collection of Category objects CategoryCollection subcategories = category.getChildren(); Example 11-5. Retrieving Subcategories of a given Category 11.2.6.
132 Chapter 11. Services Tutorials System.out.println("object = " + object.getObject()); Iterator parents = object.getParentCategories().iterator(); while(parents.hasNext()) { System.out.println("parent = " + ((Category) parents.next()).toString()); } } Example 11-6. Retrieving child objects of a given Category 11.3.
Chapter 11. Services Tutorials 133 // Attach some content fetched from a URL URL url = new URL("http://www.yahoo.com/"); MessagePart part = new MessagePart("Yahoo", "Yahoo index page"); part.setDataHandler(new DataHandler(url)); msg.attach(part); // Attach a simple text document msg.attach("Some additional text", "file.txt", "description); // Save the message msg.save(); // Send the message to the recipient User to = getRecipient(); Notification n = new Notification(to, msg); n.save(); 11.3.3.
134 Chapter 11. Services Tutorials 11.3.4. Email Alerts The examples above assume that applications will simply need to notify users of some event via email. However, one of the powerful features of the notification service is that it is built on top of the WAF messaging service. One of the goals of this design is to make it efficient for messaging applications to send Message objects directly to users via email, without the need to store any additional information in the database.
Chapter 11. Services Tutorials 135 an email alert going out to 1000 users will occupy the same amount of space in the database as a single email. 11.4. Workflow Tutorial This tutorial covers various tasks that can be accomplished with the workflow service — a simple workflow, adding a task, rolling back a task, and completing a task. 11.4.1. Simple Workflow These steps describe the procedure in Procedure 11.1, Creating a Simple Workflow.
136 Chapter 11. Services Tutorials public HelloWorldTask(OID oid) throws DataObjectNotFoundException { super(oid); } public HelloWorldTask(BigDecimal id) throws DataObjectNotFoundException { this(new OID(BASE_DATA_OBJECT_TYPE, id)); } protected HelloWorldTask(ObjectType type) { super(type); } protected HelloWorldTask(String typeName) { super(typeName); } // Add functionality in these methods public void enableEvt() { System.out.println(getLabel()+": I am enabled"); } public void disableEvt() { System.out.
Chapter 11. Services Tutorials super; } update { super; } delete { super; } } 4. In your package initializer section, create and add a new instantiator. DomainObjectInstantiator instHelloWorldTask = new ACSObjectInstantiator() { public DomainObject doNewInstance(DataObject dataObject) { return new HelloWorldTask(dataObject); } }; DomainObjectFactory.registerInstantiator( "com.arsdigita.workflow.simple.", instHelloWorldTask); 5. Create the workflow template.
138 Chapter 11. Services Tutorials 11.4.2. Adding a Task to a Workflow To add a task, you call the addTask method in workflow. The task is inactive when created, and can be set to be active by calling the setActive(true). A task is not considered part of the workflow until it becomes active. Tasks are created in an inactive state to allow you to preview them before altering the workflow state. Once a task is active, it can affect the workflow and other dependent tasks.
Chapter 11. Services Tutorials 139 versioned object type Quux { BigInteger[1..1] id = quuces.id INTEGER; String[1..1] name = quuces.name VARCHAR; object key(id); } Example 11-7. The versioned keyword The consequences of marking an object type as versioned are explained in Section 11.5.2 Fully versioned types. 11.5.2. Fully versioned types All instances of the object type Quux defined in Example 11-7 are versioned.
140 Chapter 11. Services Tutorials 11.5.3. Recoverable types The case of compound attributes like country is a little more complex. Let’s look at Example 11-8 again. Suppose the Great Quux had Wonderland listed as his country of residence on Jan 19. On Apr 12, his country of residence changed to Eriador; the Wonderland data object was deleted altogether.
Chapter 11. Services Tutorials 141 11.5.5. PDL Syntax To specify which object types should be versioned, developers can use the keywords versioned and unversioned. Consider the following example. versioned object type VT1 { BigInteger[1..1] id = t_vt1.id INTEGER; object key(id); } object type VT2 extends VT1 { component UT3[0..n] ut3s = join t_vt2.id to t_ut3.vt2_id; reference key(t_vt2.id); } object type C1 { BigInteger[1..1] id = t_c1.id INTEGER; A1[1..1] a1s = join t_c1.a1_id to t_a1.
142 Chapter 11. Services Tutorials } versioned object type VTC3 { BigInteger[1..1] id = t_vtc3.id INTEGER; object key(id); } object type C2 { BigInteger[1..1] id = t_c2.id INTEGER; composite VTC3[1..1] vtc3 = join t_c2.composite_id to t_vtc3.id; object key(id); } object type UT5 { BigInteger[1..1] id = t_ut5.id INTEGER; object key(id); } object type UT6 { BigInteger[1..1] id = t_ut6.id INTEGER; object key(id); } Example 11-9.
Chapter 11. Services Tutorials 143 Figure 11-8.
144 Chapter 11. Services Tutorials Based on Figure 11-8, the versioning service decides which object types should be versioned, recoverable, or simply ignored. The result of this decision can be visualized as follows. UT2 C2 rqd,cst:vtc3 VUT2 vnd:ut6srqd:ut1 UT6 UT1 VT1 VTC3 unv,cnt:ut5 UT5 rqd:ut3attr VT2 cnt:ut3s rqd,cst:vt2 cnt:c1s UT3 C1 rqd:a1s A1 cnt:ut4 rqd:ut4 UT4 Figure 11-9.
Chapter 11. Services Tutorials 145 Gray nodes represent types that are ignored by the versioning service. There are two kinds of edges in Figure 11-9: 1. Unlabeled edges represent the subtype relationship between two nodes. For example, VUT2 extends UT2. 2. Labeled edges are those where the edge tail is an object type that has an attribute whose type is the edge head. For example, VT2 has the component attribute c1s of type C1.
146 Chapter 11. Services Tutorials String[1..1] name = frobs.frob_name VARCHARA; } In the above example, the versioning system will ignore changes to the type attribute of the Frob object type. 5. The current versioning service provides the ability to compute a diff between any two points in history of a versioned data object. The old service did not provide a similar capability. 11.6. Search Tutorial This section provides useful tutorials for the search service. 11.6.1.
Chapter 11. Services Tutorials 147 public String getSummary(DomainObject dobj) { Note note = (Note)dobj; // Truncate body text & remove any HTML. return StringUtils.truncateString(note.getBody(), 200, true); } ... Example 11-10. Core search metadata 11.6.1.2. Supplementary search metadata The next set of optional methods allow for provision of auditing data that will be useful for narrowing search results, namely creation and modification date and the party who performed these two actions.
148 Chapter 11. Services Tutorials 11.6.1.3. Content provision The remaining method to be implemented in the MetadataProvider interface is the one that actually provides the searchable content. There are currently three formats in which searchable content can be provided, each of which are identified by a static constant in the com.arsdigita.search.ContentType class. Each search indexer will support a different set of formats, so applications should implement as many formats as make sense.
Chapter 11. Services Tutorials 149 public String getTag() { return "Body Text"; } public ContentType getType() { return ContentType.RAW; } public byte[] getBytes() { String body = m_note.getBody(); // Wrap the body HTML in a header and footer return (" html> head> title>" + m_note.getTitle() + " /title> /head> body>" + body + " /body> /html>").getBytes(); } } ... Example 11-13.
150 Chapter 11. Services Tutorials package com.example.binder; import com.arsdigita.runtime.CompoundInitializer; import com.arsdigita.runtime.DomainInitEvent; import com.arsdigita.search.MetadataProviderRegistry; public class NoteInitializer extends CompoundInitializer { public void init(DomainInitEvent evt) { super.init(evt); MetadataProviderRegistry.registerAdapter( Note.BASE_DATA_OBJECT_TYPE, new NoteMetadataProvider()); } } Example 11-15. Registering a provider 11.6.2.
Chapter 11. Services Tutorials 151 11.6.2.2. Displaying results The next stage is to add functionality for processing the query specification and displaying the results. The ResultsPane takes care of both of these tasks, only requiring an implementation of the QueryGenerator interface be passed into its constructor, which of course is already fulfilled by the QueryComponent class. ResultPane results = new ResultPane(query); add(results); Example 11-17. Adding a result pane 11.6.2.3.
152 Chapter 11. Services Tutorials private QueryComponent m_query; private FilterComponent m_typeFilter; private ResultsPane m_results; public NoteSearchComponent() { super("note:search", Note.XML_NS); m_typeFilter = new ObjectTypeFilterComponent(Note.BASE_DATA_OBJECT_TYPE); m_query = new BaseQueryComponent(); m_query.add(m_typeFilter); m_results = new ResultsPane(m_query); Form form = new Form("search"); form.add(m_query): form.
Chapter 12. Presentation (Bebop) Tutorial This chapter provides useful tutorials for learning and exercising the presentation system using Bebop, as introduced in Chapter 5 WAF Component: Presentation. The focus is primarily on integrating other presentation methods such as CSS, CSL and JSP into WAF, while demonstrating how to use Bebop. This chapter does not provide any general tutorials on these other tools. For a starting point to finding tutorials about these methods, see Chapter 14 References. 12.1.
154 Chapter 12. Presentation (Bebop) Tutorial stream from an outside source. This section discusses methods for handling this pre-formatted text intelligently. WAF provides an explicit technique for this situation by using the Bebop Label component and by disabling output escaping in the XSLT processor for the context of the label. The code: Label l = new Label("This is a b new /b l.generateXML(pageState, element); label.
Chapter 12. Presentation (Bebop) Tutorial 155 12.3. Site-Wide Master Pages Usually the pages within the scope of a single application share a common layout. For example, all of the pages may have the same four-panel layout: the same header, footer, and sidebar content, while the main content of the page varies. Another way of looking at this is that the main content of each page is enclosed in another master page. There are two aspects to making shared-layout pages like this: 1.
156 Chapter 12. Presentation (Bebop) Tutorial protected void addContents(Element layout, PageState ps) { Element topPanel = new Element("socksite:top", SOCKSITE_XML_NS); layout.addContent(topPanel); m_top.generateXML(ps, topPanel); Element sidePanel = new Element("socksite:side", SOCKSITE_XML_NS); layout.addContent(sidePanel); m_side.generateXML(ps, sidePanel); Element bottomPanel = new Element("socksite:bottom", SOCKSITE_XML_NS); layout.addContent(bottomPanel); m_bottom.
Chapter 12. Presentation (Bebop) Tutorial 157 You would use this Page like any other, for example, Page p = new SockPuppetPage(); p.add(...); .... However, when you call buildDocument, the generated DOM will include all of the content from the header, footer, etc.: " bebop:page # " " ... some page boilerplate ... ## socksite:top bebop:label SockPuppet.com: dynamic page header. /bebop:label /socksite:top " " " " ## ## You requested: ... socksite:side bebop:label SockPuppet.
158 $ $ $ Chapter 12. Presentation (Bebop) Tutorial /table % xsl:apply-templates select="bebop:bottom"/ /xsl:template % % This template must be associated with the appropriate application (for example, the main SockSite application) or site node. 12.4. Varying a Shared Layout Once you have a shared-layout Page subclass, such as SockPuppetPage above, you will likely have single pages that vary the boilerplate content slightly.
Chapter 12. Presentation (Bebop) Tutorial 159 */ private class BannerAd extends SimpleComponent { public void generateXML(PageState ps, Element parent) { Element elt = new Element("socksite:bannerAd", SOCKSITE_XML_NS); elt.setText(BannerAdManager.getInstance().getBanner()); parent.addContent(elt); } } } You must also amend the XSLT stylesheet: & !-- match the layout element and put the header, footer, etc., in their appropriate places.
160 Chapter 12. Presentation (Bebop) Tutorial HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { RequestContext rctx = DispatcherHelper.getRequestContext(req); String url = rctx.getRemainingURLPart(); // serve page as usual, except for URLs foo/*; those get a // special-case stylesheet if (!url.startsWith("foo/")) { super.servePage(p, req, resp); } else { // use custom stylesheet for foo/* // in reality, probably cache Transformer for performance Stylesheet fooSS = ..
Chapter 12. Presentation (Bebop) Tutorial 161 only certain components of PermissionsPane, you must subclass it and overwrite the following public methods: public public public public void void void void register(Page p), showAdmin(PageState s), showGrant(PageState s), showNoResults(PageState s). The register determines which and how many components you are going to use. The showFoo methods manage visibility of components.
162 Chapter 12. Presentation (Bebop) Tutorial 12.7.1. Implementing and Registering a Process Listener Your process listener must be a named Java class in order for it to be used by a persistent form. If your form listener needs certain parameters with specific names and data types, ensure that the listener implements the com.arsdigita.formbuilder.AttributeMetaDataProvider interface. You register the listener by going to the formbuilder admin interface mounted under /formbuilder.
Chapter 12. Presentation (Bebop) Tutorial Page applicationPage = new Page(); applicationPage.add(new MetaForm() { public MetaForm() { super("application_form_name"); } public Form buildForm(PageState pageState) { SimpleQuestionnaire questionnaire = new SimpleQuesionnaire(new BigDecimal("773")); return questionnaire.createComponent(); } } ); applicationPage.lock(); Example 12-2.
164 Chapter 12.
Chapter 13. Web Applications Tutorial 13.1. Support for Globalization This section discusses the internationalization (I18n) features of Java and WAF, and how to use this to localize web applications. See Chapter 14 References for more references discussing the concepts of globalization. WAF supports globalization in a variety of ways: 1. It allows the user to select a preferred Locale, either by registering a preference with WAF, or by sending the appropriate Accept-Language HTTP header with each request.
166 Chapter 13. Web Applications Tutorial Note If an application built on WAF relies on a service provided by another package, that package must also be globalized and must support any languages the WAF application is intended to support. For example, if your application relies on the places service to relate and/or present geographical data, you must ensure that this service supports all the Locales that you wish your application to support.
Chapter 13. Web Applications Tutorial 167 For example, PropertyResourceBundles might appear as follows for a HelloWorld application, in English, Spanish, and French: HelloWorldResources_en.properties hello_world=Hello world! HelloWorldResources_es.properties hello_world=Hola todo el mundo! HelloWorldResources_fr.properties hello_world=Salut tout le monde! For dynamic resources, the MessageCatalog class is provided. Dynamic resources are those that change while the server is running.
168 Chapter 13. Web Applications Tutorial 3 class. It also contains a localize(java.util.Locale) method, which is used to localize the resource to a particular Locale. You should use this class to represent all localizable data for your application. In Section 13.5 Globalization and Bebop, you will learn how to pass GlobalizedMessages to Bebop components so that they will perform the lookup when it is time to display the localized resource to the user.
Chapter 13. Web Applications Tutorial 169 // print in English: System.out.println((String) message.localize(new Locale("en", "", ""))); // print in Spanish: System.out.println((String) message.localize(new Locale("es", "", ""))); // print in French: System.out.println((String) message.localize(new Locale("fr", "", ""))); The output will appear as follows: This is a red wine. Este es un vino rojo. C’est un vin rouge.
170 Chapter 13. Web Applications Tutorial import com.arsdigita.dispatcher.Dispatcher; import com.arsdigita.globalization.GlobalizedMessage; public class HelloWorldDispatcher extends Dispatcher { // do some stuff, set up URL to method map, etc. private Page buildHelloWorldPage() { Label message = new Label(new GlobalizedMessage( "hello_world", "com.arsdigita.helloworld.HelloWorldResources" )); Page page = new Page(message); page.add(message); page.
Chapter 13. Web Applications Tutorial 171 msg.send(); Sending Rich Text Messages Rich text messages (i.e. HTML) require using the setBody(html,alternate) method to specify both the HTML and plain text versions of a message to send out: , - , - Mail msg = new Mail(to, from, subject); msg.setBody(" p This is the HTML body /p ", "This is the plain text body"); msg.send(); Attachments Message attachments are handled using one of the various attach(...) methods.
172 Chapter 13.
Chapter 14. References The following references are pointers to additional information that is relevant to WAF but beyond the scope of this guide. Martin Fowler’s book Patterns of Enterprise Application Architecture covers many of the design patterns used in WAF. For more information, see http://www.martinfowler.com/books.html#eaa. For more information regarding object-relational persistence see the following third-party links: • http://www.rational.com/products/whitepapers/296.jsp • http://www.ambysoft.
174 Chapter 14.
III. Appendixes Table of Contents A. Bebop Tag Library Reference................................................................................................... 177 B. PL/SQL Standards ..................................................................................................................... 181 C. Java Standards ........................................................................................................................... 187 D. PDL Syntax...........................................
Appendix A. Bebop Tag Library Reference A.1. Bebop/JSP . Tag show:all/ Description / Simplifies showing all the components of a page. One such directive is the equivalent of including a show:component directive for each component you have defined for your page . . . / show:page [pageClass=...] [pmClass=...] [master=...] /show:page / show:component name=... / .. show:form name=... /show:form .. show:list name=... ... show:listItem/ ...
178 Appendix A. Bebop Tag Library Reference A.2. Available Page Definition Tags Caution These tags are highly experimental. 2 Tag Description define:page name="..." title="..." [pageClass="..."] The define:page tag is the top-level tag for Bebop-JSP integration, and it is responsible for several processing actions: — Declaring a Bebop Page object. — Generating an XML document from Bebop. — Putting this XML document into the request attributes for the show:page tags.
Appendix A. Bebop Tag Library Reference 4 Tag Description 5 define:option name=... [value=...] [selected=...] [bundle=...] / 4 179 define:component classname=... 5 defines an option within a select widget or checkbox/radio group. Creates a component of the given class and adds it at the current location in the page. Requires that the component class have a no-argument constructor. Table A-2.
180 Appendix A.
Appendix B. PL/SQL Standards Like any other part of WAF, PL/SQL code must be maintainable and professional. This means that it must be consistent and therefore must abide by certain standards. The standards will ensure that our product will be useful long after the current people building and maintaining it are around. This chapter addresses some standards and guidelines that will help us achieve this goal.
182 Appendix B. PL/SQL Standards 8 9 4. Always qualify end statements. The end statement for a package should be end package_name ;, not just end;. The same is true for procedures, functions, package bodies, and triggers. 5. Always use the show errors SQL*Plus command after each PL/SQL block. It will help you debug when there are compilation errors in your PL/SQL code. 6. Name parameters as simply as possible. That is, use the column name if the parameter corresponds to a table column.
Appendix B. PL/SQL Standards 183 The function above takes the optional argument object_id. Do this to allow people to use the same API call when they are doing double-click protection. That is, they have already gotten an object_id and now they want to create the object with that object_id. B.3. Coding Style Some general style guidelines to follow for the purpose of consistency across applications. 1. Standard indentation is four spaces.
184 Appendix B. PL/SQL Standards 2. Truncate the column name until it fits.
Appendix B. PL/SQL Standards 185 Execution Plan ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE 1 0 NESTED LOOPS 2 1 TABLE ACCESS (FULL) OF ’CONSTRAINT_NAMING_EXAMPLE’ 3 1 INDEX (UNIQUE SCAN) OF ’SYS_C00140971’ (UNIQUE) The SYS_C00140971 by itself provides no information as to which index is being used in this query, and more importantly, the name of this constraint will vary from database to database. Mark Lindsey (
186 Appendix B. PL/SQL Standards @ Extending this process farther, there should be a single upgrade script for all of WAF per version. This upgrade script lives in kernel/sql/oracle-se/upgrade and is called core-platform- oldversion-name - new-version-name .sql. A @ A Similar to core-platform-create.sql, this script will not have its own SQL commands, but will simply source other files. Updating this script is the responsibility of each component developer.
Appendix C. Java Standards A good developer knows that there is more to development than programming. A great developer knows that there is more to development than development. (Ambler, 1999) The purpose of this document is to establish a set of standards for any code written in Java for use in WAF. Why have conventions? • Most of the lifetime cost of a piece of code is maintenance. • Over its lifetime, code is usually maintained by people other than the original author.
188 Appendix C. Java Standards 5. Acronyms, such as JDBC or URL, should always be capitalized when used. For instance, write JDBCLoader instead of JdbcLoader. This makes it clear that the letters are part of an acronym and is easier to read because it matches how it is referred to in English prose. Abbreviations that are not acronyms should have their first letter capitalized, with the exception of ID. 6. When debugging, avoid using System.out.println or System.err.println.
Appendix D. PDL Syntax This chapter introduces the syntax for the Persistence Definition Language. D.1. PDL Grammar The following is an Extended Backus-Naur Form (EBNF) grammar that defines the current PDL language. EBNF is a standard notation for formally defining a language that extends BNF (they are functionally equivalent -- EBNF simply introduces some elements for better readability).
190 Appendix D.
Appendix D. PDL Syntax 191 idpath := id ( id )* id := integer := D.2. PDL Reserved Words D.2.1. PDL Keywords The following list, in conjunction with the Section D.2.2 PDL Attribute Types, details all PDL reserved words. Explanations are provided where applicable. Examples of how to use almost all of these keywords can be found in the Chapter 9 Persistence Tutorial. D.2.1.1.
192 IMPORT INSERT JOIN MAP MODEL OBJECTKEY OBJECTTYPE OPTION OPTIONS QUERY REFERENCEKEY REMOVE RETRIEVE RETURNS SUPER TO TRUE UNIQUE UNVERSIONED UPDATE VERSIONED WS Appendix D. PDL Syntax = = = = = = = = = = = = = = = = = = = = = = import insert join map model object key object type option options query reference key remove retrieve returns super to true unique unversioned update versioned ([ , \t, \n, \r, \f])+ Punctuation ----------SEMI = ; EQ = = DOT = .
Appendix D. PDL Syntax • component 193 — this word is reserved for future use. — used to signify a definition of a composite relationship between the current Object Type and another Object Type. This is used in the same fashion as the association keyword. • composite • data operation — the keyword used to signify the beginning of a SQL block that is used outside of an object type.
194 Appendix D. PDL Syntax • retrieve — used to signify the beginning of the block of code defining the retrieve event. One example of this would be a code block specifying how to retrieve an object of the defined type. It can also be used with the keyword all to retrieve all objects of the defined type. • returns — used to signify that a definition returns something. One example is returns within a Data Query definition.
Appendix D. PDL Syntax model tutorial; object type Publication { BigDecimal id = publications.publication_id INTEGER; String name = publications.name VARCHAR(400); object key (id); } object type Magazine extends Publication { // we need to specify the size of the String attribute so we know // whether it is actually a String or if it is really a Clob String issueNumber = magazines.
196 Appendix D. PDL Syntax object type screenName { BigDecimal id = screen_names.name_id INTEGER; String screenName = screen_names.screen_name VARCHAR(700); Blob screenIcon = screen_names.screen_icon BLOB; object key (id); } object type Author { BigInteger[1..1] id = authors.author_id INTEGER; String[1..1] firstName = author.first_name VARCHAR(700); String[1..1] lastName = author.last_name VARCHAR(700); Blob[0..1] portrait = authors.
Appendix D. PDL Syntax and magazine_article_map.article_id = article_author_map.article_id and article_author_map.author_id = authors.author_id } map { // here we map the attributes of the objects // to columns returned by the query. magazine.name = publications.name; magazine.issueNumber = magazines.issue_number; magazine.id = publications.publication_id; author.authorID = authors.author_id; author.firstName = authors.first_name; author.lastName = authors.
198 Appendix D. PDL Syntax BigDecimal articleID; options { WRAP_QUERIES = false; } do { select max(article_id) from articles } map { articleID = articles.article_id; } } query UsersGroups { String firstName; String lastName; String groupName; do{ select * from users, groups, membership where users.user_id = membership.member_id and membership.group_id = groups.group_id } map { firstName=users.first_name; lastName=users.last_name; groupName=groups.
Appendix D. PDL Syntax where relation_type = :relationType) connect by prior related_category_id = category_id start with category_id = :categoryID) m, cat_categories c where c.category_id = m.related_category_id } map { level = m.l; categoryID = c.category_id; name = c.name; description = c.description; isEnabled = c.enabled_p; } } Example D-1.
200 Appendix D.
Appendix D. PDL Syntax DataQueryPLSQLFunction(v_article_id in integer) return number is v_title varchar(700); begin select title into v_title from articles where article_id = v_article_id; return v_title; end; / show errors Example D-2.
202 Appendix D.
Persistence Glossary The terms used by the persistence system are largely based upon the Unified Modeling Language (UML) terminology. For background information, please consult the UML documentation at http://www.rational.com/uml/. Association Associations are bi-directional semantic connections. Put another way, they represent the relationship between two separate objects (UML Classes). Objects within an association are completely independent, though they may be linked together.
204 Persistence Glossary Domain Object Domain objects are not part of the persistence layer, but since they are the most common way to access a Data Object (which is part of the persistence layer), it is useful to discuss them here. Domain objects contain logic particular to a business domain, such as managing users or credit cards. This logic operates over a persistent data object that contains the state necessary to implement the domain-specific logic.
Persistence Glossary 205 Link Attributes A Link is the path from one object to another. Associations are types of links. A Link Attribute is a property of this link. For instance, if a developer wanted to know when the link was created, he could store the creationDate as a Link Attribute. See Section 9.3.4 Link Attributes and Section 9.8 Link Attributes.
206 Persistence Glossary F G select code and tables here where secondtable.second_object_id_one = firsttable.object_id_one and secondtable.second_object_id_two = firsttable.object_id_two See Also: Object Key. Roles Roles describe how one class is associated with another. One or both classes in the association may have roles. For example, a User has a "Member" role for the group (that is, a user is a member of a group). See Also: Role Reference.
Persistence Glossary 207 UML Class A UML Class is a description of a set of objects that share the same attributes, operations, and relationships. A UML Class is almost identical to a Java class.
208 Persistence Glossary
Index (See master pages) building with Eclipse, 40 A acronyms presentation, 19 ACSObject base permissioning class, 11 API mail, 16 appenders custom WAF, 47 logging, 47 Application BaseServlet defined, 31 properties, 31 applications determining Locale, 165 dispatcher defined, 32 notes PropertyResourceBundle, 166 ResourceBundle, 166 overview, 6 Servlet, mapping to, 32 WAF base class, 31 architecture overview, 2 Association, 203 Association Block Example, 70 Composite Example, 71 Defining, 70 Java Example, 7
210 logging concepts, 42 problems with System.out.
211 J Java coding standards, 187 references, 188 JavaServer Pages (See JSP) Join Element, 204 Join Path, 204 Join Paths, 89 JSP Bebop integration, 28 dynamic pages, 29 extending BaseJSP, 31 Presentation, 25 static pages, 29 manipulating in runtime, 45 M mail (See mail service) API, 16 service concept, 16 mail attachments (See mail service) mail service K kernel overview, 3 resources, 12 users, groups and parties defined, 11 L language determining local, 165 language localization (See globalization) Lif
212 O Object Persistence, 9 Object Key Defined, 206 Example, 65 Object Type Inheritance, 69 Model, 65 Object-Relational Mapping, 9 Ordering Examples, 79 How-To, 85 output streams problem with buffered, 47 P Parameter Binding How-To, 86 parties (See users, groups and parties) PDL (See Persistence Definition Language (PDL)) Defined, 9 grammar, 189 PDL Keywords - escaping reserved words, 191 PDL Reserved Words - List, 191 Perforce and Eclipse, 40 performance and logging, 48 permissions ACSObject, 11 concept
213 S U search service concept, 17 service auditing, concepts of, 13 categorization, concepts of, 13 formbuilder, concepts of, 14 globalization, concepts of, 15 mail, concepts of, 16 messaging, concepts of, 16 notification, concepts of, 17 search, concepts of, 17 versioning, concepts of, 18 workflow, concepts of, 18 services portal, concept of, 17 Servlet Applications, mapping to, 32 Session Management, 10 shared layout (See master pages) standards presentation, 19 structured Data parsing, 19 stylesheets
214 X XSL Presentation Conventions, 20 XSLT Bebop, using with, 153 globalization, 170 implementing, 153 overriding default stylesheet, 159 using with WAF, 20
Colophon The Red Hat Applications manuals are written in DocBook SGML v4.1 format. The HTML and PDF formats are produced using custom DSSSL stylesheets and custom jade wrapper scripts. The DocBook SGML files are primarily written in Emacs with the help of PSGML mode; additional authoring and editing has been done with vi using macros and key mappings. Garrett LeSage created the admonition graphics (note, tip, important, caution, and warning). They may be freely redistributed with the Red Hat documentation.
216