[seam-commits] Seam SVN: r8141 - trunk/doc/Seam_Reference_Guide/en-US.
seam-commits at lists.jboss.org
seam-commits at lists.jboss.org
Thu May 8 12:49:06 EDT 2008
Author: pete.muir at jboss.org
Date: 2008-05-08 12:49:06 -0400 (Thu, 08 May 2008)
New Revision: 8141
Modified:
trunk/doc/Seam_Reference_Guide/en-US/Concepts.xml
trunk/doc/Seam_Reference_Guide/en-US/Configuration.xml
trunk/doc/Seam_Reference_Guide/en-US/Controls.xml
trunk/doc/Seam_Reference_Guide/en-US/Events.xml
trunk/doc/Seam_Reference_Guide/en-US/Framework.xml
trunk/doc/Seam_Reference_Guide/en-US/Itext.xml
trunk/doc/Seam_Reference_Guide/en-US/Mail.xml
trunk/doc/Seam_Reference_Guide/en-US/Security.xml
trunk/doc/Seam_Reference_Guide/en-US/Testing.xml
Log:
Merge in doc changes from trunk
Modified: trunk/doc/Seam_Reference_Guide/en-US/Concepts.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Concepts.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Concepts.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -268,7 +268,9 @@
</para>
<para>
Since the session context is multithreaded, and often contains volatile state, session scope
- components are always protected by Seam from concurrent access. Seam serializes requests to session
+ components are always protected by Seam from concurrent access so long as the Seam interceptors
+ are not disabled for that component. If interceptors are disabled, then any thread-safety that is
+ required must be implemented by the component itself. Seam serializes requests to session
scope session beans and JavaBeans by default (and detects and breaks any deadlocks that occur). This is
not the default behaviour for application scoped components however, since application scoped components
do not usually hold volatile state and because synchronization at the global level is
@@ -357,7 +359,9 @@
the page or stateless contexts.
</para>
<para>
- Concurrent requests to session-scoped stateful session beans are always serialized by Seam.
+ Concurrent requests to session-scoped stateful session beans are always serialized by Seam as long
+ as the Seam interceptors are not disabled for the bean.
+
</para>
<para>
Seam stateful session bean components may be instantiated using <literal>Component.getInstance()</literal>
Modified: trunk/doc/Seam_Reference_Guide/en-US/Configuration.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Configuration.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Configuration.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -68,6 +68,15 @@
<param-value>.xhtml</param-value>
</context-param>]]></programlisting>
+ <para>
+ If you are using facelets in JBoss AS, you'll find that facelets
+ logging is broken. Seam provides a bridge to fix this, to use it
+ copy <literal>lib/interop/jboss-seam-jul.jar</literal> to
+ <literal>$JBOSS_HOME/server/default/deploy/jboss-web.deployer/jsf-libs/</literal>
+ and include the <literal>jboss-seam-ui.jar</literal> in the
+ <literal>WEB-INF/lib</literal> of your application.
+ </para>
+
</sect2>
<sect2>
@@ -959,5 +968,131 @@
</para>
</sect1>
+
+ <sect1>
+ <title>Deploying custom resources</title>
+
+ <para>
+ Seam scans all jars containing <literal>/seam.properties</literal>,
+ <literal>/META-INF/components.xml</literal> or <literal>/META-INF/seam.properties</literal>
+ on startup for resources. For example, all classes annotated with
+ <literal>@Name</literal> are registered with Seam as Seam components.
+ </para>
+
+ <para>
+ You may also want Seam to handle custom resources. A common use case
+ is to handle a specific annotation and Seam provides specific
+ support for this. First, tell Seam which annotations to handle in
+ <literal>/META-INF/seam-deployment.properties</literal>:
+ </para>
+
+ <programlisting><![CDATA[# A colon-separated list of annotation types to handle
+org.jboss.seam.deployment.annotationTypes=com.acme.Foo:com.acme.Bar]]></programlisting>
+
+ <para>
+ Then, during application startup you can get hold of all classes
+ annotated with <literal>@Foo</literal>:
+ </para>
+
+ <programlisting><![CDATA[@Name("fooStartup")
+ at Scope(APPLICATION)
+ at Startup
+public class FooStartup {
+ @In("#{deploymentStrategy.annotatedClasses['com.acme.Foo']}")
+ private Set<Class<Object>> fooClasses;
+
+ @In("#{hotDeploymentStrategy.annotatedClasses['com.acme.Foo']}")
+ private Set<Class<Object>> hotFooClasses;
+
+ @Create
+ public void create() {
+ for (Class clazz : fooClasses) {
+ handleClass(clazz);
+ }
+ for (Class clazz : hotFooClasses) {
+ handleClass(clazz);
+ }
+ }
+
+}]]></programlisting>
+
+ <para>
+ You can also handle <emphasis>any</emphasis> resource. For example,
+ you process any files with the extension <literal>.foo.xml</literal>.
+ To do this, we need to write a custom deployment handler:
+ </para>
+
+ <programlisting><![CDATA[public class FooDeploymentHandler implements DeploymentHandler {
+
+ private Set<InputStream> files = new HashSet<InputStream>();
+
+ public String getName() {
+ return "fooDeploymentHandler";
+ }
+
+ public Set<InputStream> getFiles() {
+ return files;
+ }
+
+ public void handle(String name, ClassLoader classLoader) {
+ if (name.endsWith(".foo.xml")) {
+ files.add(classLoader.getResourceAsStream(name));
+ }
+ }
+}]]></programlisting>
+
+ <para>
+ Here we are just building a list of any files with the suffix
+ <literal>.foo.xml</literal>.
+ </para>
+
+ <para>
+ Then, we need to register the deployment handler with Seam. In
+ <literal>/META-INF/seam-deployment.properties</literal>:
+ </para>
+
+ <programlisting><![CDATA[# For standard deployment
+org.jboss.seam.deployment.deploymentHandlers=com.acme.FooDeploymentHandler
+# For hot deployment
+org.jboss.seam.deployment.hotDeploymentHandlers=com.acme.FooDeploymentHandler]]></programlisting>
+
+ <para>
+ You can register multiple deployment handler using a comma
+ separated list.
+ </para>
+
+ <para>
+ Seam uses deployment handlers internally to install components and
+ namespaces, therefore the <literal>handle()</literal> is called too
+ early in inside Seam bootstrap to normally be useful. However, you
+ can easily access the deployment handler during an
+ <literal>APPLICATION</literal> scoped component's startup:
+ </para>
+
+ <programlisting><![CDATA[@Name("fooStartup")
+ at Scope(APPLICATION)
+ at Startup
+public class FooStartup {
+
+ @In("#{deploymentStrategy['fooDeploymentHandler']}")
+ private MyDeploymentHandler myDeploymentHandler;
+
+ @In("#{hotDeploymentStrategy['fooDeploymentHandler']}")
+ private MyDeploymentHandler myHotDeploymentHandler;
+
+ @Create
+ public void create() {
+ for (InputStream is : myDeploymentHandler.getFiles()) {
+ handleFooXml(is);
+ }
+ for (InputStream is : myHotDeploymentHandler.getFiles()) {
+ handleFooXml(is);
+ }
+ }
+
+}]]></programlisting>
+
+ </sect1>
+
</chapter>
Modified: trunk/doc/Seam_Reference_Guide/en-US/Controls.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Controls.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Controls.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -96,9 +96,14 @@
<para>
You can specify both <literal>view</literal> and
<literal>action</literal> on <literal><s:link /></literal>.
- In this case, the action wil be called once the redirect to the
+ In this case, the action will be called once the redirect to the
specified view has occured.
</para>
+
+ <para>
+ The use of action listeners (including the default JSF action
+ listener) is not supported with <literal><s:button /></literal>.
+ </para>
</section>
@@ -145,6 +150,11 @@
form.</emphasis>
</para>
+ <para>
+ The use of action listeners (including the default JSF action
+ listener) is not supported with <literal><s:link /></literal>.
+ </para>
+
<para><emphasis>Attributes</emphasis></para>
<itemizedlist>
<listitem>
@@ -299,13 +309,14 @@
<para><emphasis>Description</emphasis></para>
<para>
Assigns an entity converter to the current component. This is
- primarily useful for radio button and dropdown controls.
+ useful for radio button and dropdown controls.
</para>
<para>
- The converter works with any managed entity which has an
- <literal>@Id</literal>
- annotation - either simple or composite.
+ The converter works with any managed entity - either simple or
+ composite. The converter should be able to find the items
+ declared in the JSF controls on form submission, otherwise you
+ will receive a validation error.
</para>
<para><emphasis>Attributes</emphasis></para>
@@ -321,36 +332,59 @@
<para>
If your <emphasis>Managed Persistence Context</emphasis> isn't
- called <literal>entityManager</literal>, then you need to set it in
- components.xml:
+ called <literal>entityManager</literal>, then you need to set it
+ in components.xml:
</para>
- <programlisting role="XML"><![CDATA[<component name="org.jboss.seam.ui.EntityConverter">
- <property name="entityManager">#{em}</property>
-</component>]]></programlisting>
+ <programlisting><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
+ xmlns:ui="http://jboss.com/products/seam/ui">
+
+ <ui:entity-loader entity-manager="#{em}" />]]></programlisting>
<para>
If you are using a <emphasis>Managed Hibernate Session</emphasis>
then you need to set it in components.xml:
</para>
- <programlisting role="XML"><![CDATA[<component name="org.jboss.seam.ui.EntityConverter">
- <property name="session">#{hibernateSession}</property>
-</component>]]></programlisting>
+ <programlisting><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
+ xmlns:ui="http://jboss.com/products/seam/ui">
+
+ <ui:hibernate-entity-loader />]]></programlisting>
<para>
+ If your <emphasis>Managed Hibernate Session</emphasis> isn't
+ called <literal>session</literal>, then you need to set it
+ in components.xml:
+ </para>
+
+ <programlisting><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
+ xmlns:ui="http://jboss.com/products/seam/ui">
+
+ <ui:hibernate-entity-loader session="#{hibernateSession}" />]]></programlisting>
+
+ <para>
If you want to use more than one entity manager with the entity
converter, you can create a copy of the entity converter for each
- entity manager in components.xml:
+ entity manager in <literal>components.xml</literal> - note how
+ the entity converter delegates to the entity loader to perform
+ persistence operations:
</para>
- <programlisting role="XML"><![CDATA[<component name="myEntityConverter" class="org.jboss.seam.ui.converter.EntityConverter">
- <property name="entityManager">#{em}</property>
-</component>]]></programlisting>
+ <programlisting><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
+ xmlns:ui="http://jboss.com/products/seam/ui">
+
+ <ui:entity-converter name="standardEntityConverter" entity-loader="#{standardEntityLoader}" />
+
+ <ui:entity-loader name="standardEntityLoader" entity-manager="#{standardEntityManager}" />
- <programlisting role="XHTML"><![CDATA[<h:selectOneMenu value="#{person.continent}">
+ <ui:entity-converter name="restrictedEntityConverter" entity-loader="#{restrictedEntityLoader}" />
+
+ <ui:entity-loader name="restrictedEntityLoader" entity-manager="#{restrictedEntityManager}" />]]></programlisting>
+
+ <programlisting><![CDATA[<h:selectOneMenu value="#{person.continent}">
<s:selectItems value="#{continents.resultList}" var="continent"
label="#{continent.name}" />
- <f:converter converterId="myEntityConverter" />
+ <f:converter converterId="standardEntityConverter" />
</h:selectOneMenu>]]></programlisting>
+
<para><emphasis>Usage</emphasis></para>
<programlisting role="XHTML"><![CDATA[<h:selectOneMenu value="#{person.continent}" required="true">
@@ -380,9 +414,67 @@
noSelectionLabel="Please select" />
<s:convertEnum />
</h:selectOneMenu>]]></programlisting>
- </section>
+ </section>
<section>
+ <title><literal><s:convertAtomicBoolean></literal></title>
+
+ <para><emphasis>Description</emphasis></para>
+ <para>
+ <literal>javax.faces.convert.Converter</literal> for
+ <literal>java.util.concurrent.atomic.AtomicBoolean</literal>.
+ </para>
+
+ <para><emphasis>Attributes</emphasis></para>
+ <para>
+ None.
+ </para>
+
+ <para><emphasis>Usage</emphasis></para>
+ <programlisting><![CDATA[<h:outputText value="#{item.valid}">
+ <s:convertAtomicBoolean />
+</h:outputText>]]></programlisting>
+ </section>
+ <section>
+ <title><literal><s:convertAtomicInteger></literal></title>
+
+ <para><emphasis>Description</emphasis></para>
+ <para>
+ <literal>javax.faces.convert.Converter</literal> for
+ <literal>java.util.concurrent.atomic.AtomicInteger</literal>.
+ </para>
+
+ <para><emphasis>Attributes</emphasis></para>
+ <para>
+ None.
+ </para>
+
+ <para><emphasis>Usage</emphasis></para>
+ <programlisting><![CDATA[<h:outputText value="#{item.id}">
+ <s:convertAtomicInteger />
+</h:outputText>]]></programlisting>
+ </section>
+ <section>
+ <title><literal><s:convertAtomicLong></literal></title>
+
+ <para><emphasis>Description</emphasis></para>
+ <para>
+ <literal>javax.faces.convert.Converter</literal> for
+ <literal>java.util.concurrent.atomic.AtomicLong</literal>.
+ </para>
+
+ <para><emphasis>Attributes</emphasis></para>
+ <para>
+ None.
+ </para>
+
+ <para><emphasis>Usage</emphasis></para>
+ <programlisting><![CDATA[<h:outputText value="#{item.id}">
+ <s:convertAtomicLong />
+</h:outputText>]]></programlisting>
+ </section>
+
+ <section>
<title><literal><s:validate></literal></title>
<para><emphasis>Description</emphasis></para>
Modified: trunk/doc/Seam_Reference_Guide/en-US/Events.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Events.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Events.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -885,7 +885,7 @@
</filter-mapping>]]></programlisting>
<para>
- You may also need to disable Facelets development mode in <literal>web.xml</literal> and
+ You need to disable Facelets development mode in <literal>web.xml</literal> and
Seam debug mode in <literal>components.xml</literal> if you want your exception handlers
to fire.
</para>
@@ -997,7 +997,7 @@
<para>
<literal>org.jboss.seam.handledException</literal> holds the nested exception that
was actually handled by an exception handler. The outermost (wrapper) exception is
- also available, as <literal>org.jboss.seam.exception</literal>.
+ also available, as <literal>org.jboss.seam.caughtException</literal>.
</para>
<section>
Modified: trunk/doc/Seam_Reference_Guide/en-US/Framework.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Framework.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Framework.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -55,8 +55,7 @@
your taste, you can use extension instead:
</para>
- <programlisting role="JAVA"><![CDATA[
- at Name("personHome")
+ <programlisting role="JAVA"><![CDATA[@Name("personHome")
public class PersonHome extends EntityHome<Person> {
@In EntityManager personDatabase;
Modified: trunk/doc/Seam_Reference_Guide/en-US/Itext.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Itext.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Itext.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -95,6 +95,12 @@
and <literal>attachment</literal>, which indicates that the document should be treated as a download.
The default value is <literal>inline</literal>.</para>
</listitem>
+ <listitem>
+ <para>
+ <literal>fileName</literal> — For attachments, this value
+ overrides the downloaded file name.
+ </para>
+ </listitem>
</itemizedlist>
Modified: trunk/doc/Seam_Reference_Guide/en-US/Mail.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Mail.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Mail.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -567,6 +567,11 @@
<literal><h:graphicImage></literal> in your emails.
</para>
</listitem>
+ <listitem>
+ <para>
+ <literal>messageId</literal> — Sets the Message-ID explicitly
+ </para>
+ </listitem>
</itemizedlist>
</listitem>
</varlistentry>
Modified: trunk/doc/Seam_Reference_Guide/en-US/Security.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -1622,5 +1622,919 @@
}]]></programlisting>
</sect1>
+
+ <sect1>
+ <title>Identity Management</title>
+ <para>
+ Seam Security provides an optional identity management API, which offers the following features:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ User management - the ability to create, delete and modify user accounts and their role memberships.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Authentication of users without the need for writing an Authenticator component.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A hierarchical role/group membership structure, allowing roles to be members of other roles.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Pluggable identity store, allowing the developer to choose their security provider, whether it be
+ JPA, LDAP, Kerberos, etc.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ The core of the identity management API is the <literal>IdentityManager</literal> component. Before it can be
+ used however, it must be configured with an <literal>IdentityStore</literal> implementation. The
+ <literal>IdentityStore</literal> does the actual work of interacting with the underlying security provider,
+ whatever it may be.
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/security-identitymanager.png" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/security-identitymanager.png" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <sect2>
+ <title>Configuration</title>
+
+ <para>
+ Configuration of the <literal>IdentityManager</literal> is extremely simple, requiring only an
+ <literal>IdentityStore</literal> to be configured in <literal>components.xml</literal>.
+ The identity management namespace is <literal>http://jboss.com/products/seam/security/management</literal>
+ and its schema location is <literal>http://jboss.com/products/seam/identity-management-2.1.xsd</literal>.
+ Here's a simple example showing the configuration of a <literal>JPAIdentityStore</literal> - for the
+ <literal>IdentityManager</literal> to use it, it must be named <literal>identityStore</literal>:
+ </para>
+
+ <programlisting><![CDATA[
+ <identity-management:jpa-identity-store name="identityStore" account-class="com.acme.UserAccount"/>
+ ]]></programlisting>
+ </sect2>
+
+ <sect2>
+ <title>JPAIdentityStore</title>
+
+ <para>
+ <literal>JPAIdentityStore</literal> is an <literal>IdentityStore</literal> implementation that uses
+ JPA as its underlying security provider. User accounts and their role memberships are stored in a
+ self-referencing database table, for which the corresponding entity bean must extend
+ <literal>org.jboss.seam.security.management.UserAccount</literal> to provide the following properties:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/security-useraccount.png" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/security-useraccount.png" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ To provide a complete example, here's what the actual database tables may look like:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/security-useraccountschema.png" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/security-useraccountschema.png" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ And an example of the corresponding entity bean:
+ </para>
+
+ <programlisting><![CDATA[@Entity @Table(name = "USER_ACCOUNT")
+public class UserAccount extends org.jboss.seam.security.management.UserAccount
+ implements Serializable
+{
+ private Integer accountId;
+ private String username;
+ private String passwordHash;
+ private boolean enabled;
+ private AccountType accountType;
+ private Set<UserAccount> memberships;
+
+ @Id @GeneratedValue public Integer getAccountId() { return accountId; }
+ public void setAccountId(Integer accountId) { this.accountId = accountId; }
+
+ @NotNull @Override public String getUsername() { return username; }
+ @Override public void setUsername(String username) { this.username = username; }
+
+ @Override public String getPasswordHash() { return passwordHash; }
+ @Override public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
+
+ @Override public AccountType getAccountType() { return accountType; }
+ @Override public void setAccountType(AccountType accountType) { this.accountType = accountType; }
+
+ @Override public boolean isEnabled() { return enabled; }
+ @Override public void setEnabled(boolean enabled) { this.enabled = enabled; }
+
+ @ManyToMany(targetEntity = MemberAccount.class) @JoinTable(name = "ACCOUNT_MEMBERSHIP",
+ joinColumns = @JoinColumn(name = "ACCOUNT_ID"),
+ inverseJoinColumns = @JoinColumn(name = "MEMBER_OF"))
+ @Override public Set<UserAccount> getMemberships() { return memberships; }
+ @Override public void setMemberships(Set<UserAccount> memberships) { this.memberships = memberships; }}]]></programlisting>
+
+ <para>
+ In the above example, the implementation of <literal>UserAccount</literal> is self-referencing
+ in that it has a many-to-many relationship with itself via its <literal>memberships</literal>
+ property. To keep the model simple, both user accounts and roles are persisted as
+ <literal>UserAccount</literal>s, with the <literal>accountType</literal> property acting as the
+ discriminator between the two. With this model, roles can be members of other roles, making it
+ possible to define complex role membership hierarchies.
+ </para>
+
+ <para>
+ Once the <literal>UserAccount</literal> implementation has been created, the <literal>JPAIdentityStore</literal>
+ must be configured to use that implementation any time it performs an identity management operation.
+ This is done by specifying the <literal>account-class</literal> property in <literal>components.xml</literal>.
+ In the following example, it is configured as <literal>com.acme.UserAccount</literal>:
+ </para>
+
+ <programlisting><![CDATA[
+ <identity-management:jpa-identity-store name="identityStore" account-class="com.acme.UserAccount"/>]]></programlisting>
+
+ <para>
+ Please note that this is a required parameter, and must always be specified when using the
+ <literal>JPAIdentityStore</literal>.
+ </para>
+
+ <sect3>
+ <title>Password hashing</title>
+
+ <para>
+ The <literal>JPAIdentityStore</literal> stores a salted hash of the user's password, using the username
+ as the source material for salt generation. This guarantees that two users with the same password will
+ still have different password hashes. The method for generating a password hash is listed here for
+ convenience - it may be useful for generating password hashes for inclusion in DML scripts, etc:
+ </para>
+
+ <programlisting><![CDATA[
+ public String hashPassword(String password, String saltPhrase)
+ {
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+
+ md.update(saltPhrase.getBytes());
+ byte[] salt = md.digest();
+
+ md.reset();
+ md.update(password.getBytes("UTF-8"));
+ md.update(salt);
+
+ byte[] raw = md.digest();
+
+ return new String(Hex.encodeHex(raw));
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ ]]></programlisting>
+ </sect3>
+
+ </sect2>
+
+ <sect2>
+ <title>Authentication with the Identity Management API</title>
+
+ <para>
+ To authenticate using the Identity Management API, it is as simple as not specifying the
+ <literal>authenticate-method</literal> property for the <literal>Identity</literal> component.
+ If no <literal>authenticate-method</literal> is specified, then by default the authentication
+ process (controlled by <literal>SeamLoginModule</literal>) will attempt to authenticate using
+ <literal>IdentityManager</literal>'s <literal>authenticate()</literal> method, and no
+ Authenticator component is necessary.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Using the IdentityManager API</title>
+
+ <para>
+ The <literal>IdentityManager</literal> can be accessed either by injecting it into your Seam
+ component as follows:
+ </para>
+
+ <programlisting><![CDATA[ @In IdentityManager identityManager;]]></programlisting>
+
+ <para>
+ or by accessing it through its static <literal>instance()</literal> method:
+ </para>
+
+ <programlisting><![CDATA[ IdentityManager identityManager = IdentityManager.instance();]]></programlisting>
+
+ <para>
+ The following table describes each of the methods that <literal>IdentityManager</literal> provides:
+ </para>
+
+ <table>
+ <title>Identity Management API</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="1*" />
+ <colspec colnum="2" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Method</para>
+ </entry>
+ <entry align="center">
+ <para>Returns</para>
+ </entry>
+ <entry align="center">
+ <para>Description</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <para>
+ <literal>createUser(String name, String password)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Creates a new user account, with the specified name and password. Returns <literal>true</literal>
+ if successful, or <literal>false</literal> if not.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>deleteUser(String name)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Deletes the user account with the specified name. Returns <literal>true</literal>
+ if successful, or <literal>false</literal> if not.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>createRole(String role)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Creates a new role, with the specified name. Returns <literal>true</literal>
+ if successful, or <literal>false</literal> if not.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>deleteRole(String name)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Deletes the role with the specified name. Returns <literal>true</literal>
+ if successful, or <literal>false</literal> if not.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>enableUser(String name)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Enables the user account with the specified name. Accounts that are not enabled are
+ not able to authenticate. Returns <literal>true</literal> if successful, or
+ <literal>false</literal> if not.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>disableUser(String name)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Disables the user account with the specified name. Returns <literal>true</literal> if
+ successful, or <literal>false</literal> if not.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>changePassword(String name, String password)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Changes the password for the user account with the specified name. Returns
+ <literal>true</literal> if successful, or <literal>false</literal> if not.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>isUserEnabled(String name)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns <literal>true</literal> if the specified user account is enabled, or
+ <literal>false</literal> if it isn't.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>grantRole(String name, String role)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Grants the specified role to the specified user or role. The role must already exist for it to
+ be granted. Returns <literal>true</literal> if the role is successfully granted, or
+ <literal>false</literal> if it is already granted to the user.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>revokeRole(String name, String role)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Revokes the specified role from the specified user or role. Returns <literal>true</literal>
+ if the specified user is a member of the role and it is successfully revoked, or
+ <literal>false</literal> if the user is not a member of the role.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>userExists(String name)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns <literal>true</literal> if the specified user exists, or <literal>false</literal>
+ if it doesn't.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>listUsers()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>List</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns a list of all user names, sorted in alpha-numeric order.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>listUsers(String filter)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>List</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns a list of all user names filtered by the specified filter parameter, sorted in alpha-numeric order.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>listRoles()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>List</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns a list of all role names.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>getGrantedRoles(String name)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>List</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns a list of the names of all the roles explicitly granted to the specified user name.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>getImpliedRoles(String name)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>List</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Returns a list of the names of all the roles implicitly granted to the specified user name.
+ Implicitly granted roles include those that are not directly granted to a user, rather they are
+ granted to the roles that the user is a member of. For example, is the <literal>admin</literal>
+ role is a member of the <literal>user</literal> role, and a user is a member of the <literal>admin</literal>
+ role, then the implied roles for the user are both the <literal>admin</literal>, and <literal>user</literal>
+ roles.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>authenticate(String name, String password)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ Authenticates the specified username and password using the configured Identity Store. Returns
+ <literal>true</literal> if successful or <literal>false</literal> if authentication failed.
+ Successful authentication implies nothing beyond the return value of the method. It does not
+ change the state of the <literal>Identity</literal> component - to perform a proper Seam login the
+ <literal>Identity.login()</literal> must be used instead.
+ </para>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Using the Identity Management API requires that the calling user has the appropriate authorization to invoke
+ its methods. The following table describes the permission requirements for each of the methods in
+ <literal>IdentityManager</literal>.
+ </para>
+
+ <table>
+ <title>Identity Management Security Permissions</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="1*" />
+ <colspec colnum="2" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Method</para>
+ </entry>
+ <entry align="center">
+ <para>Permission Name</para>
+ </entry>
+ <entry align="center">
+ <para>Permission Action</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>
+ <para>
+ <literal>createUser()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>create</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>deleteUser()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>delete</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>createRole()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>create</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>deleteRole()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>delete</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>enableUser()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>update</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>disableUser()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>update</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>changePassword()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>update</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>isUserEnabled()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>read</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>grantRole()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>update</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>revokeRole()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>update</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>userExists()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>read</literal>
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>listUsers()</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>seam.account</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>read</literal>
+ </para>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ The following code listing provides an example set of security rules that grants access to all
+ Identity Management-related methods to members of the <literal>admin</literal> role:
+ </para>
+
+ <programlisting><![CDATA[rule CreateAccount
+ no-loop
+ activation-group "permissions"
+when
+ check: PermissionCheck(name == "seam.account", action == "create", granted == false)
+ Role(name == "admin")
+then
+ check.grant();
+end
+
+rule ReadAccount
+ no-loop
+ activation-group "permissions"
+when
+ check: PermissionCheck(name == "seam.account", action == "read", granted == false)
+ Role(name == "admin")
+then
+ check.grant();
+end
+
+rule UpdateAccount
+ no-loop
+ activation-group "permissions"
+when
+ check: PermissionCheck(name == "seam.account", action == "update", granted == false)
+ Role(name == "admin")
+then
+ check.grant();
+end
+
+rule DeleteAccount
+ no-loop
+ activation-group "permissions"
+when
+ check: PermissionCheck(name == "seam.account", action == "delete", granted == false)
+ Role(name == "admin")
+then
+ check.grant();
+end]]></programlisting>
+
+ </sect2>
+
+ <sect2>
+ <title>Seam-gen and Identity Management</title>
+
+ <para>
+ When creating a new project using seam-gen (see <xref linkend="gettingstarted"/>), by default the
+ <literal>IdentityManager</literal> will be configured with a <literal>JPAIdentityStore</literal>
+ and a <literal>UserAccount</literal> implementation will be generated as part of the new project.
+ In addition to this, the project will include the following user management screens, allowing
+ new users to be created, roles assigned, etc:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/security-usermanager1.png" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/security-usermanager1.png" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+ <para>
+ The user detail screen:
+ </para>
+
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/security-usermanager2.png" align="center"/>
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="../shared/images/security-usermanager2.png" align="center"/>
+ </imageobject>
+ </mediaobject>
+
+
+ </sect2>
+
+ </sect1>
+
</chapter>
Modified: trunk/doc/Seam_Reference_Guide/en-US/Testing.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Testing.xml 2008-05-08 15:59:42 UTC (rev 8140)
+++ trunk/doc/Seam_Reference_Guide/en-US/Testing.xml 2008-05-08 16:49:06 UTC (rev 8141)
@@ -530,10 +530,14 @@
</para>
<para>
- You need to provide a dataset for DBUnit. IMPORTANT NOTE: DBUnit supports two
- formats for dataset files, flat and XML. Seam's DBUnitSeamTest assumes the flat
- format is used, so please ensure that your dataset is in this format also.
+ You need to provide a dataset for DBUnit.
</para>
+
+ <caution>
+ DBUnit supports two formats for dataset files, flat and XML. Seam's
+ DBUnitSeamTest assumes the flat format is used, so make sure that
+ your dataset is in this format.
+ </caution>
<programlisting role="XML"><![CDATA[<dataset>
@@ -581,11 +585,30 @@
<![CDATA[<parameter name="datasourceJndiName" value="java:/seamdiscsDatasource"/>]]>
</programlisting>
- <para>
- DBUnitSeamTest only works out of the box with HSQL as a datasource.
- If you want to use another database, then you'll need to implement
- some extra methods. Read the javadoc on
- <literal>DBUnitSeamTest</literal> for more.
+ <para>
+ DBUnitSeamTest has support for MySQL and HSQL - you need to tell it
+ which database is being used:
+ </para>
+
+ <programlisting><![CDATA[<parameter name="database" value="HSQL" />]]></programlisting>
+
+ <para>
+ It also allows you to insert binary data into the test data set (n.b.
+ this is untested on Windows). You need to tell it where to locate
+ these resources:
+ </para>
+
+ <programlisting><![CDATA[<parameter name="binaryDir" value="images/" />]]></programlisting>
+
+ <para>
+ You <emphasis>must</emphasis> specify these three parameters in your
+ <literal>testng.xml</literal>.
+ </para>
+
+ <para>
+ If you want to use DBUnitSeamTest with another database, you'll need
+ to implement some methods. Read the javadoc of
+ <literal>AbstractDBUnitSeamTest</literal> for more.
</para>
</section>
@@ -593,9 +616,9 @@
<section id="testing.mail">
<title>Integration Testing Seam Mail</title>
- <para>
+ <caution>
Warning! This feature is still under development.
- </para>
+ </caution>
<para>
It's very easy to integration test your Seam Mail:
More information about the seam-commits
mailing list