You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2005/06/19 18:06:29 UTC

cvs commit: jakarta-tapestry/framework/src/test/org/apache/tapestry/enhance TestInjectPageWorker.java

hlship      2005/06/19 09:06:29

  Modified:    framework/src/java/org/apache/tapestry/enhance
                        EnhanceMessages.java EnhanceStrings.properties
               .        status.xml
               framework/src/descriptor/META-INF tapestry.enhance.xml
               src/documentation/content/xdocs/UsersGuide injection.xml
                        listenermethods.xml
  Added:       framework/src/java/org/apache/tapestry/enhance
                        InjectPageWorker.java
               framework/src/test/org/apache/tapestry/enhance
                        TestInjectPageWorker.java
  Log:
  Add injection of pages.
  
  Revision  Changes    Path
  1.17      +6 -0      jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhanceMessages.java
  
  Index: EnhanceMessages.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhanceMessages.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- EnhanceMessages.java	16 Jun 2005 12:03:17 -0000	1.16
  +++ EnhanceMessages.java	19 Jun 2005 16:06:29 -0000	1.17
  @@ -112,4 +112,10 @@
                   .getJavaClassName(propertyType), ClassFabUtils.getJavaClassName(requiredType));
       }
   
  +    public static String wrongTypeForPageInjection(String propertyName, Class propertyType)
  +    {
  +        return _formatter.format("wrong-type-for-page-injection", propertyName, ClassFabUtils
  +                .getJavaClassName(propertyType));
  +    }
  +
   }
  \ No newline at end of file
  
  
  
  1.14      +2 -1      jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhanceStrings.properties
  
  Index: EnhanceStrings.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhanceStrings.properties,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- EnhanceStrings.properties	16 Jun 2005 12:03:17 -0000	1.13
  +++ EnhanceStrings.properties	19 Jun 2005 16:06:29 -0000	1.14
  @@ -23,4 +23,5 @@
   incompatible-inject-type=The value obtained using locator ''{0}'' ({1}) is not compatible with the existing property (of type {2}).
   initial-value-for-property=initial value for property {0}
   unknown-inject-type=Unable to create injected property {0}: injection type ''{1}'' is not defined.
  -wrong-type-for-property=Property {0} is type {1}, which is not compatible with {2}.
  \ No newline at end of file
  +wrong-type-for-property=Property {0} is type {1}, which is not compatible with {2}.
  +wrong-type-for-page-injection=Property {0} is type {1}, which is not compatible with injection. The property type should be Object, IPage, or a specific page class.
  \ No newline at end of file
  
  
  
  1.1                  jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/InjectPageWorker.java
  
  Index: InjectPageWorker.java
  ===================================================================
  // Copyright 2005 The Apache Software Foundation
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package org.apache.tapestry.enhance;
  
  import java.lang.reflect.Modifier;
  
  import org.apache.hivemind.ApplicationRuntimeException;
  import org.apache.hivemind.Location;
  import org.apache.hivemind.service.BodyBuilder;
  import org.apache.hivemind.service.MethodSignature;
  import org.apache.tapestry.IPage;
  import org.apache.tapestry.spec.InjectSpecification;
  
  /**
   * Injects code to access a named page within the application.
   * 
   * @author Howard Lewis Ship
   * @since 4.0
   */
  public class InjectPageWorker implements InjectEnhancementWorker
  {
      public void performEnhancement(EnhancementOperation op, InjectSpecification spec)
      {
          performEnhancement(op, spec.getObject(), spec.getProperty(), spec.getLocation());
      }
  
      public void performEnhancement(EnhancementOperation op, String pageName, String propertyName,
              Location location)
      {
          Class propertyType = op.getPropertyType(propertyName);
  
          if (propertyType == null)
              propertyType = Object.class;
          else if (propertyType.isPrimitive())
              throw new ApplicationRuntimeException(EnhanceMessages.wrongTypeForPageInjection(
                      propertyName,
                      propertyType), null, location, null);
  
          op.claimProperty(propertyName);
  
          MethodSignature sig = new MethodSignature(propertyType, op
                  .getAccessorMethodName(propertyName), null, null);
  
          BodyBuilder builder = new BodyBuilder();
  
          builder.add("return ");
  
          // If the property type is not IPage or a superclass of IPage then a cast
          // is needed.
  
          if (!propertyType.isAssignableFrom(IPage.class))
              builder.add("({0})", propertyType.getName());
  
          builder.add("getPage().getRequestCycle().getPage(\"{0}\");", pageName);
  
          op.addMethod(Modifier.PUBLIC, sig, builder.toString());
      }
  }
  
  
  
  1.137     +1 -0      jakarta-tapestry/status.xml
  
  Index: status.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/status.xml,v
  retrieving revision 1.136
  retrieving revision 1.137
  diff -u -r1.136 -r1.137
  --- status.xml	19 Jun 2005 15:00:45 -0000	1.136
  +++ status.xml	19 Jun 2005 16:06:29 -0000	1.137
  @@ -85,6 +85,7 @@
          <action type="add" dev="HLS">Add translator binding prefix.</action>
          <action type="add" dev="HLS">Add cancel and refresh listener parameters to Form.</action>
          <action type="add" dev="HLS">Listener methods may now return a page name, or a page instance, to activate and render the response.</action>
  +       <action type="add" dev="HLS">Add injection of pages.</action>
       </release>
       <release version="4.0-alpha-3" date="May 16 2005">
         <action type="add" dev="HLS">Add initial support for the validator: binding prefix.</action>
  
  
  
  1.21      +1 -0      jakarta-tapestry/framework/src/descriptor/META-INF/tapestry.enhance.xml
  
  Index: tapestry.enhance.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/descriptor/META-INF/tapestry.enhance.xml,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -u -r1.20 -r1.21
  --- tapestry.enhance.xml	9 Jun 2005 18:33:44 -0000	1.20
  +++ tapestry.enhance.xml	19 Jun 2005 16:06:29 -0000	1.21
  @@ -114,6 +114,7 @@
       <worker type="state" object="service:InjectStateWorker"/>
       <worker type="meta" object="service:InjectMetaWorker"/>
       <worker type="script" object="service:InjectScriptWorker"/>
  +    <worker type="page" object="instance:InjectPageWorker"/>
     </contribution>
     
     <service-point id="DispatchToInjectWorker" interface="EnhancementWorker">
  
  
  
  1.2       +84 -24    jakarta-tapestry/src/documentation/content/xdocs/UsersGuide/injection.xml
  
  Index: injection.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/src/documentation/content/xdocs/UsersGuide/injection.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- injection.xml	15 May 2005 22:56:37 -0000	1.1
  +++ injection.xml	19 Jun 2005 16:06:29 -0000	1.2
  @@ -31,7 +31,8 @@
   <p>
   Tapestry 4.0 introduces an entirely new concept into Tapestry application development: property injection.
   By use of the &spec.inject; element in page and component specifications, it is possible
  -to add new properties to pages or components.
  +to add new properties to pages or components, using the &spec.inject; element in the page
  +or component specification.
   </p>    
   
   <p>
  @@ -40,35 +41,28 @@
   </p>
   
   <p>
  -There are four types of property injection built into Tapestry.  These types correspond to the
  -type attribute of the &spec.inject; element:
  +There are different <em>types</em> of injected properties, defined by the type attribute of the &spec.inject;
  +element. The type determines how the object attribute is interpreted, and otherwise guides how code
  +for the property is generated at runtime.  The default type is <strong>object</strong>.
   </p>
   
  -<dl>
  -  <dt>object</dt>
  -  <dd>
  -    Injects <link href="hivemind.html">objects defined by the HiveMind Registry</link>.
  -  </dd>
  -  <dt>state</dt>
  -  <dd>
  -    Creates a property providing access to an &aso;.
  -  </dd>
  -  <dt>meta</dt>
  -  <dd>
  -    Creates properties that allow access to component meta-data properties, and can automatically
  -    convert values from strings to other types.
  -  </dd>
  -  <dt>script</dt>
  -  <dd>
  -    Creates a property containing a parsed &IScript;, ready to execute.
  -  </dd>
  -</dl>
  +<p>
  +Like so much in Tapestry, this list is open to extension.  The 
  +<link href="&hivedoc;/config/tapestry.enhance.InjectWorkers.html">tapestry.enhance.InjectWorkers</link> configuration point
  +defines the available types of injection.
  +The <link href="&hivedoc;/config/tapestry.enhance.EnhancementWorkers.html">tapestry.enhance.EnhancementWorkers</link> configuration
  +point defines an entire pipeline used to perform runtime code enhancement (of which property injection
  +is a critical phase).
  +</p>
   
   <p>
  -Like so much in Tapestry, this list is open to extension.  The tapestry.enhance.InjectWorkers configuration point
  -defines new types, and the HiveMind services that implement those types.
  +In addition, many other elements support a property attribute; this is the name of a property to create
  +that holds the corresponding object.  For example, the &spec.bean; element's property allows access
  +to a managed bean; the bean is <em>still</em> created on first reference.  Components and assets
  +may also be injected in this way.
   </p>
   
  +
   <section id="injection.meta">
     <title>meta injection</title>
     
  @@ -115,6 +109,62 @@
     
   </section> <!-- injection.meta -->
   
  +<section id="injection.object">
  +  <title>object injection</title>
  +  
  +<p>
  +The most common kind of injection, because "object" is the default injection type. The object is a HiveMind object.
  +The <link href="hivemind.html">HiveMind integration documentation</link> covers this type
  +of injection in more detail.
  +</p>
  +
  +<p>
  +
  +</p>
  +
  +</section> <!-- injection.object -->
  +
  +<section id="injection.page">
  +  <title>page injection</title>
  +  
  +<p>
  +Page injection allows a page to be injected into another page (or component). Beneather the covers,
  +the logic simply accesses the &IRequestCycle; object and obtains the page from it, and adds a
  +cast if necessary.
  +</p>
  +
  +<p>
  +The property type can be Object, &IPage;, or any type assignable to &IPage;.
  +</p>
  +
  +<p>
  +  This is often used with &listener-method;s.  For example:
  +</p>
  +
  +<source><![CDATA[
  +  <inject property="detailsPage" type="page" object="Details"/>
  +]]></source>
  +
  +<source>
  +  public abstract Details getDetailsPage();
  +  
  +  public IPage doShowDetails(long productId)
  +  {
  +    Details details = getDetailsPage();
  +    
  +    details.setProductId(productId);
  +    
  +    return details;
  +  }
  +</source>
  +  
  +<p>
  +This is a common idiom: getting a page and casting it to its real type, invoking methods upon it,
  +then returning it (from the listener method), so that it is activated to render the response.
  +</p>
  +  
  +</section> <!-- injection.page -->
  +
   <section id="injection.script">
     <title>script injection</title>
     
  @@ -151,6 +201,16 @@
     getScript().execute(cycle, pageRenderSupport, _symbols);
   </source>
   
  +</section> <!-- injection.script -->
  +
  +<section id="injection.state">
  +  <title>state injection</title>
  +  
  +<p>
  +This style of injection allows easy access to <link href="state.html#state.aso">Application State Objects</link>,
  +objects which store various kinds of global information (information needed on many pages).
  +</p>  
  +  
   </section>
       
     </body>
  
  
  
  1.6       +33 -7     jakarta-tapestry/src/documentation/content/xdocs/UsersGuide/listenermethods.xml
  
  Index: listenermethods.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/src/documentation/content/xdocs/UsersGuide/listenermethods.xml,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- listenermethods.xml	19 Jun 2005 15:00:45 -0000	1.5
  +++ listenermethods.xml	19 Jun 2005 16:06:29 -0000	1.6
  @@ -50,15 +50,34 @@
   </note>
   
   <p>
  -A listener method is a public method. It may take parameters, or may not ... the rules are discussed below. 
  -A simple listener method take no parameters, or takes a single parameter of type &IRequestCycle;.  
  -A listener method may return void, return a string (the name of a page), or return &IPage; (or an object 
  -assignable to &IPage;).  In
  -the latter two cases, the returned page  will be <em>activated</em> to render the response. This means that
  -in the vast majority of cases, you can write listener methods <em>without</em> the &IRequestCycle; object.
  +A listener method is always a public  instance method.   It may take parameters, or not, and may
  +return void or certain other types.
  +</p>
  +
  +<section id="listenermethods.return">
  +  <title>Return Type</title>
  +  
  +<p>
  +A listener method may return void, may return a string, or may return an object that implements &IPage;.
  +The last two options are used to change the <em>active page</em>, the page which will render
  +the response. Returning null will not change the active page (it defaults to the page containing the
  +link or form components which invoked the listener method).
   </p>
   
   <p>
  +This control over the returned page, especially when combined with
  +<link href="injection.html#injection.page">page injection</link>, means that you will rarely
  +need to access the &IRequestCycle; object.
  +</p>
  +
  +</section> <!-- listenermethods.return -->
  +
  +
  +
  +<section id="listenermethods.parameters">
  +  <title>Listener Method Parameters</title>
  +
  +<p>
   When using the &DirectLink; component, you may specify additional <em>listener parameters</em>.  The listener parameters
   are encoded into the URL and will be available in a later request, when the listener is triggered.
   </p>
  @@ -141,12 +160,18 @@
   the parameter values (so you can use <code>int</code> and <code>java.lang.Integer</code> interchangeably).
   </p>
   
  +</section> <!-- listenermethods.parameters -->
  +
  +<section id="listenermethods.invoking">
  +  <title>Invoking Listener Methods</title>
  +
   
   <p>
   When creating components that accept a listener as a parameter, you should not invoke the
   &IActionListener; directly, instead, you should inject the infrastructure:ListenerInvoker service 
   into your component, and have it invoke the listener.  The ListenerInvoker is extensible, and 
  -application logic may depend on ListenerInvoker's behavior.
  +application logic may depend on ListenerInvoker's behavior (commonly, it is used to mark
  +transactions boundaries).
   </p>
   
   <p>
  @@ -181,5 +206,6 @@
   when the listener is an optional parameter.
   </p>
   
  +</section> <!-- listenermethods.invoking -->
   </body>
   </document>
  
  
  
  1.1                  jakarta-tapestry/framework/src/test/org/apache/tapestry/enhance/TestInjectPageWorker.java
  
  Index: TestInjectPageWorker.java
  ===================================================================
  // Copyright 2005 The Apache Software Foundation
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package org.apache.tapestry.enhance;
  
  import java.lang.reflect.Modifier;
  
  import org.apache.hivemind.ApplicationRuntimeException;
  import org.apache.hivemind.Location;
  import org.apache.hivemind.service.MethodSignature;
  import org.apache.hivemind.test.HiveMindTestCase;
  import org.apache.tapestry.html.BasePage;
  import org.apache.tapestry.spec.InjectSpecification;
  import org.apache.tapestry.spec.InjectSpecificationImpl;
  import org.easymock.MockControl;
  
  /**
   * Tests for {@link org.apache.tapestry.enhance.InjectPageWorker}.
   * 
   * @author Howard Lewis Ship
   * @since 4.0
   */
  public class TestInjectPageWorker extends HiveMindTestCase
  {
      public void testPrimitivePropertyType()
      {
          Location l = newLocation();
  
          MockControl opc = newControl(EnhancementOperation.class);
          EnhancementOperation op = (EnhancementOperation) opc.getMock();
  
          op.getPropertyType("somePage");
          opc.setReturnValue(int.class);
  
          replayControls();
  
          InjectSpecification is = new InjectSpecificationImpl();
          is.setProperty("somePage");
          is.setObject("SomePage");
          is.setLocation(l);
  
          try
          {
              new InjectPageWorker().performEnhancement(op, is);
              unreachable();
          }
          catch (ApplicationRuntimeException ex)
          {
              assertEquals(
                      "Property somePage is type int, which is not compatible with injection. The property type should be Object, IPage, or a specific page class.",
                      ex.getMessage());
              assertSame(l, ex.getLocation());
          }
  
          verifyControls();
      }
  
      /**
       * Test for when there's no existing property.
       */
  
      public void testNoPropertyType()
      {
          MockControl opc = newControl(EnhancementOperation.class);
          EnhancementOperation op = (EnhancementOperation) opc.getMock();
  
          op.getPropertyType("somePage");
          opc.setReturnValue(null);
  
          op.claimProperty("somePage");
  
          op.getAccessorMethodName("somePage");
          opc.setReturnValue("getSomePage");
  
          MethodSignature sig = new MethodSignature(Object.class, "getSomePage", null, null);
  
          op.addMethod(
                  Modifier.PUBLIC,
                  sig,
                  "return getPage().getRequestCycle().getPage(\"SomePage\");");
  
          replayControls();
  
          InjectSpecification is = new InjectSpecificationImpl();
          is.setProperty("somePage");
          is.setObject("SomePage");
  
          new InjectPageWorker().performEnhancement(op, is);
  
          verifyControls();
      }
  
      public void testExistingPropertyType()
      {
          MockControl opc = newControl(EnhancementOperation.class);
          EnhancementOperation op = (EnhancementOperation) opc.getMock();
  
          op.getPropertyType("somePage");
          opc.setReturnValue(BasePage.class);
  
          op.claimProperty("somePage");
  
          op.getAccessorMethodName("somePage");
          opc.setReturnValue("getSomePage");
  
          MethodSignature sig = new MethodSignature(BasePage.class, "getSomePage", null, null);
  
          // BasePage is too specific (the getPage() method returns IPage),
          // so we see a cast.
  
          op
                  .addMethod(
                          Modifier.PUBLIC,
                          sig,
                          "return (org.apache.tapestry.html.BasePage)getPage().getRequestCycle().getPage(\"SomePage\");");
  
          replayControls();
  
          InjectSpecification is = new InjectSpecificationImpl();
          is.setProperty("somePage");
          is.setObject("SomePage");
  
          new InjectPageWorker().performEnhancement(op, is);
  
          verifyControls();
      }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-dev-help@jakarta.apache.org