Changeset 4206
- Timestamp:
- Apr 4, 2008, 8:42:33 AM (15 years ago)
- Location:
- trunk
- Files:
-
- 7 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/build.xml
r4205 r4206 1389 1389 <mkdir dir="${docbook.html.out}/admindoc/installation_upgrade"/> 1390 1390 <mkdir dir="${docbook.html.out}/admindoc/plugin_installation"/> 1391 <mkdir dir="${docbook.html.out}/admindoc/extensions"/> 1391 1392 <mkdir dir="${docbook.html.out}/admindoc/coreplugin_configuration"/> 1392 1393 <mkdir dir="${docbook.html.out}/developerdoc"/> -
trunk/doc/src/docbook/admindoc/index.xml
r3679 r4206 31 31 <include file="installation_upgrade.xml"/> 32 32 <include file="plugin_installation.xml"/> 33 <include file="extensions.xml" /> 33 34 <include file="user_administration.xml"/> 34 35 </part> -
trunk/doc/src/docbook/developerdoc/api_overview.xml
r4093 r4206 2982 2982 </sect1> 2983 2983 2984 <sect1 id="api_overview.extensions"> 2985 <title>Extensions API</title> 2986 2987 <sect2 id="api_overview.extensions.core"> 2988 <title>The core part</title> 2989 2990 <para> 2991 The <emphasis>Extensions API</emphasis> is divided into two parts. A core 2992 part and a web client specific part. The core part can be found in the 2993 <package>net.sf.basedb.util.extensions</package> package and it's sub-packages. 2994 The core part consists of three sub-parts: 2995 </para> 2996 2997 <itemizedlist> 2998 <listitem> 2999 <para> 3000 A set of interface definitions which forms the core of the Extensions API. 3001 The interfaces defines, for example, what an <interfacename 3002 docapi="net.sf.basedb.util.extensions">Extension</interfacename> is and 3003 what an <interfacename 3004 docapi="net.sf.basedb.util.extensions">ActionFactory</interfacename> should do. 3005 </para> 3006 </listitem> 3007 3008 <listitem> 3009 <para> 3010 A <classname docapi="net.sf.basedb.util.extensions">Registry</classname> that is 3011 used to keep track of installed extensions. The registry also provides 3012 functionality for invoking and using the extensions. 3013 </para> 3014 </listitem> 3015 3016 <listitem> 3017 <para> 3018 Utility classes that are useful when implementation a client application 3019 that can be extendable. The most useful example is the <classname 3020 docapi="net.sf.basedb.util.extensions.xml">XmlLoader</classname> which can 3021 read extension definitions from XML files and create the proper factories, 3022 etc. 3023 </para> 3024 </listitem> 3025 </itemizedlist> 3026 3027 <figure id="core_api.figures.extensions_core"> 3028 <title>The core part of the Extensions API</title> 3029 <screenshot> 3030 <mediaobject> 3031 <imageobject> 3032 <imagedata 3033 align="center" 3034 fileref="figures/uml/corelayer.extensions_core.png" format="PNG" /> 3035 </imageobject> 3036 </mediaobject> 3037 </screenshot> 3038 </figure> 3039 3040 <para> 3041 The <classname docapi="net.sf.basedb.util.extensions">Registry</classname> 3042 is one of the main classes in the extension system. All extension points and 3043 extensions must be registered before they can be used. Typically, you will 3044 first register extension points and then extensions, beacuse an extension 3045 can't be registered until the extension point it is extending has been 3046 registered. 3047 </para> 3048 3049 <para> 3050 An <interfacename docapi="net.sf.basedb.util.extensions">ExtensionPoint</interfacename> 3051 is an ID and a definition of an <interfacename docapi="net.sf.basedb.util.extensions">Action</interfacename> 3052 class. The other options (name, description, renderer factory, etc.) are optional. 3053 An <interfacename docapi="net.sf.basedb.util.extensions">Extension</interfacename> 3054 that extends a specific extension point must provide an 3055 <interfacename docapi="net.sf.basedb.util.extensions">ActionFactory</interfacename> 3056 instance that can create actions of the type the extension point requires. 3057 </para> 3058 3059 <example> 3060 <title>The menu extensions point</title> 3061 <para> 3062 The <code>net.sf.basedb.clients.web.menu.extensions</code> extension point 3063 requires <interfacename 3064 docapi="net.sf.basedb.clients.web.extensions.menu">MenuItemAction</interfacename> 3065 objects. An extension for this extension point must provide a factory that 3066 can create <classname>MenuItemAction</classname>:s. BASE ships with default 3067 factory implementations, for example the <classname 3068 docapi="net.sf.basedb.clients.web.extensions.menu">FixedMenuItemFactory</classname> 3069 class, but an extension may provide it's own factory implementation if it wants to. 3070 </para> 3071 </example> 3072 3073 <para> 3074 Call the <methodname>Registry.useExtensions()</methodname> method 3075 to use extensions from one or several extension points. This method will 3076 find all extensions for the given extension points. If a filter is given, 3077 it checks if any of the extensions or extension points has been disabled. 3078 It will then call <methodname>ActionFactory.prepareContext()</methodname> 3079 for all remaining extensions. This gives the action factory a chance to 3080 also disable the extension, for example, if the logged in user doesn't 3081 have the required permissions. The action factory may also set attributes 3082 on the context. The attributes can be anything that the extension point 3083 may make use of. Check the documentation for the specific extension point 3084 for information about which attributes it supports. If there are 3085 any renderer factories, their <methodname>RendererFactory.prepareContext()</methodname> 3086 is also called. They have the same possibility of setting attributes 3087 on the context, but can't disable an extension. 3088 </para> 3089 3090 <para> 3091 After this, an <classname 3092 docapi="net.sf.basedb.util.extensions">ExtensionsInvoker</classname> 3093 object is created and returned to the extension point. Note that 3094 the <methodname>ActionFactory.getActions()</methodname> has not been 3095 called yet, so we don't know if the extensions are actually 3096 going to generate any actions. The <methodname>ActionFactory.getActions()</methodname> 3097 is not called until we have got ourselves an 3098 <classname docapi="net.sf.basedb.util.extensions">ActionIterator</classname> 3099 from the <methodname>ExtensionsInvoker.iterate()</methodname> method and 3100 starts to iterate. The call to <methodname>ActionIterator.hasNext()</methodname> 3101 will propagate down to <methodname>ActionFactory.getActions()</methodname> 3102 and the generated actions are then available with the 3103 <methodname>ActionIterator.next()</methodname> method. 3104 </para> 3105 3106 <para> 3107 The <methodname>ExtensionsInvoker.renderDefault()</methodname> 3108 and <methodname>ExtensionsInvoker.render()</methodname> are 3109 just convenience methods that will make it easer to render 3110 the actions. The first method will of course only work if the 3111 extension point is providing a renderer factory, that can 3112 create the default renderer. 3113 </para> 3114 3115 <note> 3116 <title>Be aware of multi-threading issues</title> 3117 <para> 3118 When you are creating extensions you must be aware that 3119 multiple threads may access the same objects at the same time. 3120 In particular, any action factory or renderer factory has to be 3121 thread-safe, since only one exists for each extension. 3122 Action and renderer objects should be thread-safe if the 3123 factories re-use the same objects. 3124 </para> 3125 </note> 3126 3127 </sect2> 3128 3129 <sect2 id="api_overview.extensions.web"> 3130 <title>The web client part</title> 3131 3132 <para> 3133 The web client specific parts of the Extensions API can be found 3134 in the <package>net.sf.basedb.client.web.extensions</package> package 3135 and it's subpackages. The top-level package contains classes used to 3136 administrate the extension system. Here is for example the 3137 <classname docapi="net.sf.basedb.client.web.extensions">ExtensionsControl</classname> 3138 class which is the master controller for the web client extensions. It: 3139 </para> 3140 3141 <itemizedlist> 3142 <listitem> 3143 <para> 3144 Keeps track of installed extensions and which JAR or XML file they are 3145 installed from. 3146 </para> 3147 </listitem> 3148 3149 <listitem> 3150 <para> 3151 Can, manually, or automatically, find and install new or 3152 updated extensions and uninstall deleted extensions. 3153 </para> 3154 </listitem> 3155 3156 <listitem> 3157 <para> 3158 Adds permission control to the extension system, so that only an 3159 administrator is allowed to change settings, enable/disable extensions, 3160 etc. 3161 </para> 3162 </listitem> 3163 </itemizedlist> 3164 3165 <para> 3166 In the top-level package there are also some abstract classes that may 3167 be useful to extend for developers creating their own extensions. 3168 For example, we recommend that all action factories extend the <classname 3169 docapi="net.sf.basedb.client.web.extensions">AbstractJspActionFactory</classname> 3170 class. 3171 </para> 3172 3173 <para> 3174 The sub-packages to <package>net.sf.basedb.client.web.extensions</package> 3175 are mostly specific to a single extension point or to a specific type of 3176 extension point. The <package>net.sf.basedb.client.web.extensions.menu</package> 3177 package, for example, contains classes that are/can be used for extensions 3178 adding menu items to the <menuchoice><guimenu>Extensions</guimenu></menuchoice> 3179 menu. 3180 </para> 3181 3182 <figure id="core_api.figures.extensions_web"> 3183 <title>The web client part of the Extensions API</title> 3184 <screenshot> 3185 <mediaobject> 3186 <imageobject> 3187 <imagedata 3188 align="center" 3189 fileref="figures/uml/corelayer.extensions_web.png" format="PNG" /> 3190 </imageobject> 3191 </mediaobject> 3192 </screenshot> 3193 </figure> 3194 3195 <para> 3196 When the web server is staring up, the <classname>ExtensionsServlet</classname> 3197 is automatically loaded by Tomcat. This servlet has as it's only 3198 task to initialise the extensions system by calling 3199 <methodname>ExtensionsControl.init()</methodname>. This will result in 3200 an initial scan for installed extensions, which is equivalent to doing 3201 a manual scan with the force update setting to false. This means that 3202 the extension system is up an running as soon as the first user log's 3203 in to BASE. 3204 </para> 3205 3206 <para> 3207 Using extensions only involves calling the 3208 <methodname>ExtensionsControl.createContext()</methodname> and 3209 <methodname>ExtensionsControl.useExtensions()</methodname> methods. This 3210 returns an <classname docapi="net.sf.basedb.util.extensions">ExtensionsInvoker</classname> 3211 object as described in the previous section. 3212 </para> 3213 3214 <para> 3215 To render the actions it is possible to either use the 3216 <methodname>ExtensionsInvoker.iterate()</methodname> method 3217 and generate HTML from the information in each action. Or 3218 (the better way) is to use a renderer together with 3219 <classname docapi="net.sf.basedb.clients.web.taglib.extensions">Render</classname> 3220 taglib. 3221 </para> 3222 3223 <para> 3224 To get information about the installed extensions, 3225 change settings, enabled/disable extensions, performing a manual 3226 scan, etc. use the <methodname>ExtensionsControl.get()</methodname> 3227 method. This will create a permission-controlled object. All 3228 users has read permission, administrators has write permission. 3229 </para> 3230 3231 <note> 3232 <para> 3233 The permission we check for is WRITE permission on the 3234 web client item. This means it is possible to give a user 3235 permissions to manage the extension system by assigning 3236 WRITE permission to the web client entry in the database. 3237 Do this from <menuchoice> 3238 <guimenu>Administrate</guimenu> 3239 <guimenuitem>Clients</guimenuitem> 3240 </menuchoice>. 3241 </para> 3242 </note> 3243 3244 </sect2> 3245 3246 </sect1> 3247 2984 3248 <sect1 id="api_overview.other_api"> 2985 3249 <title>Other useful classes and methods</title> -
trunk/doc/src/docbook/developerdoc/extensions.xml
r4187 r4206 27 27 --> 28 28 29 <chapter id="extensions ">29 <chapter id="extensions_developer"> 30 30 <?dbhtml dir="extensions"?> 31 <title>Extensions </title>32 33 <sect1 id="extensions .overview">31 <title>Extensions developer</title> 32 33 <sect1 id="extensions_developer.overview"> 34 34 <title>Overview</title> 35 35 <para> … … 49 49 extensions. From this page, if you are logged in with enough permissions, 50 50 it is also possible to configure the extensions system, enable/disable 51 extensions, etc. 51 extensions, etc. Read more about this in <xref linkend="admin.extensions" />. 52 52 </para> 53 53 … … 60 60 </para> 61 61 62 <sect2 id="extensions .examplecode">62 <sect2 id="extensions_developer.examplecode"> 63 63 <title>Download code examples</title> 64 64 … … 70 70 </sect2> 71 71 72 <sect2 id="extensions .terminology">72 <sect2 id="extensions_developer.terminology"> 73 73 <title>Terminology</title> 74 74 … … 190 190 </sect1> 191 191 192 <sect1 id="extensions .helloworld">192 <sect1 id="extensions_developer.helloworld"> 193 193 <title>Hello world as an extension</title> 194 194 … … 337 337 338 338 339 <sect1 id="extensions .factories">339 <sect1 id="extensions_developer.factories"> 340 340 <title>Custom action factories</title> 341 341 … … 612 612 </programlisting> 613 613 614 <note> 615 <title>Be aware of multi-threading issues</title> 616 <para> 617 When you are creating custom action and renderer factories be 618 aware that multiple threads may use a single factory instance 619 at the same time. Action and renderer objects only needs to 620 be thread-safe if the factories re-use the same objects. 621 </para> 622 </note> 623 614 624 </sect1> 615 625 616 <sect1 id="extensions .resources">626 <sect1 id="extensions_developer.resources"> 617 627 <title>Custom images, JSP files, and other resources</title> 618 628 … … 694 704 </para> 695 705 696 <sect2 id="extensions .resources.scripts">706 <sect2 id="extensions_developer.resources.scripts"> 697 707 <title>Javascript and stylesheets</title> 698 708 … … 760 770 </sect1> 761 771 762 <sect1 id="extensions .renderer">772 <sect1 id="extensions_developer.renderer"> 763 773 <title>Custom renderers and renderer factories</title> 764 774 765 775 <para> 766 TODO 767 </para> 768 776 It is always the responsibility of the extension point to 777 render an action. The need for custom renderers is typically 778 very small, at least if you want your extensions to blend into 779 the look and feel of the BASE web client. Most customizations can 780 be probably be handled by stylesheets and images. That said, 781 you may still have a reason for using a custom renderer. 782 </para> 783 784 <para> 785 Renderer factories are not very different from action factories. 786 They are specified in the same way in the XML file and uses the 787 same method for initialisation, including support for path 788 conversion, etc. The difference is that you use a 789 <sgmltag class="starttag">renderer-factory</sgmltag> tag 790 instead of an <sgmltag class="starttag">action-factory</sgmltag> 791 tag. 792 </para> 793 794 <programlisting language="xml"> 795 <![CDATA[ 796 <renderer-factory> 797 <factory-class> 798 ... some factory class ... 799 </factory-class> 800 <parameters> 801 ... some parameters ... 802 </parameters> 803 </renderer-factory> 804 ]]> 805 </programlisting> 806 807 <para> 808 A <interfacename docapi="net.sf.basedb.util.extensions" 809 >RendererFactory</interfacename> also has a <methodname>prepareContext()</methodname> 810 method that can be used to tell the web client about any scripts or stylesheets 811 the extension needs. If your renderer factory extends the <classname 812 docapi="net.sf.basedb.clients.web.extensions">AbstractJspRendererFactory</classname> 813 class you will not have to worry about this since you can configure 814 scripts and stylesheets in the XML file. 815 </para> 816 817 <para> 818 A render factory must also implement the <methodname>getRenderer()</methodname> 819 which should return a <interfacename docapi="net.sf.basedb.util.extensions" 820 >Renderer</interfacename> instance. The extension system will then call 821 the <methodname>Renderer.render()</methodname> method to render an action. 822 This method may be called multiple times if the extension created more than 823 one action. 824 </para> 825 826 <para> 827 The renderers responsibility is to generate the HTML 828 that is going to be sent to the web client. To do this it needs 829 access to the <classname 830 docapi="net.sf.basedb.clients.web.extensions">JspContext</classname> 831 object that was passed to the renderer factory. Here is a simple 832 outline of both a renderer factory and renderer. 833 </para> 834 835 <programlisting language="java"> 836 <![CDATA[ 837 // File: MyRendererFactory.java 838 public class MyRendererFactory 839 extends AbstractJspRendererFactory<MyAction> 840 { 841 842 public MyRendererFactory() 843 {} 844 845 @Override 846 public MyRenderer getRenderer(Context context, Extension extension) 847 { 848 return new MyRenderer((JspContext)context); 849 } 850 } 851 852 // File: MyRenderer.java 853 public class MyRenderer 854 implements Renderer<MyAction> 855 { 856 857 private final JspContext context; 858 public MyRenderer(JspContext context) 859 { 860 this.context = context; 861 } 862 863 /** 864 Generates HTML (unless invisible): 865 <a class="[clazz]" style="[style]" onclick="[onClick]">[title]</a> 866 */ 867 public void render(MyAction action) 868 { 869 if (!action.isVisible()) return; 870 Writer out = context.getOut(); 871 try 872 { 873 out.write("<a"); 874 if (action.getClazz() != null) 875 { 876 out.write(" class=\"" + action.getClazz() + "\""); 877 } 878 if (action.getStyle() != null) 879 { 880 out.write(" style=\"" + action.getStyle() + "\""); 881 } 882 if (action.getOnClick() != null) 883 { 884 out.write(" href=\"" + action.getOnClick() + "\""); 885 } 886 out.write(">"); 887 out.write(HTML.encodeTags(action.getTitle())); 888 out.write("</a>\n"); 889 } 890 catch (IOException ex) 891 { 892 throw new RuntimeException(ex); 893 } 894 } 895 } 896 ]]> 897 </programlisting> 769 898 </sect1> 899 900 <sect1 id="extensions_developer.extension_points"> 901 <title>Extension points</title> 902 903 <para> 904 The BASE web client ships with a number of predefined extension 905 points. Adding more extension points to the existing web client 906 requires some minor modifications to the regular JSP pages. But this 907 is not what this chapter is about. This chapter is about defining new 908 extension points as part of an extension. It is nothing magical about 909 this and the process is the same as for the regular extension points in 910 the web client. 911 </para> 912 913 <para> 914 The first thing you need to do is to start writing the 915 XML definition of the extension point. Here is an example 916 from the web client: 917 </para> 918 919 <programlisting language="xml"> 920 <![CDATA[ 921 <extensions 922 xmlns="http://base.thep.lu.se/extensions.xsd" 923 > 924 <extension-point 925 id="net.sf.basedb.clients.web.menu.extensions" 926 > 927 <action-class>net.sf.basedb.clients.web.extensions.menu.MenuItemAction</action-class> 928 <name>Menu: extensions</name> 929 <description> 930 Extension point for adding extensions to the 'Extensions' menu. 931 Extensions should provide MenuItemAction instances. The rendering 932 is internal and extensions can't use their own rendering factories. 933 The context will only include information about the currently logged 934 in user, not information about the current page that is displayed. 935 The reason for this is that the rendered menu is cached as a string 936 in the user session. The menu is not updated on every page request. 937 This extension point doesn't support custom stylesheets or javascripts. 938 </description> 939 </extension-point> 940 </extensions> 941 ]]> 942 </programlisting> 943 944 <para> 945 The <sgmltag class="starttag">extensions</sgmltag> tag is the root tag and 946 is needed to set up the namespace and schema validation. 947 </para> 948 949 <para> 950 The <sgmltag class="starttag">extension-point</sgmltag> defines a new 951 extension point. It must have an <sgmltag>id</sgmltag> attribute that 952 is unique among all installed extension points. We recommend using 953 the same naming conventions as for java packages. See <ulink 954 url="http://java.sun.com/docs/codeconv/html/CodeConventions.doc8.html">Java naming 955 conventions from Sun</ulink>. 956 </para> 957 958 <note> 959 <title>Document the extension point!</title> 960 <para> 961 The <sgmltag class="starttag">name</sgmltag> and 962 <sgmltag class="starttag">description</sgmltag> tags are optional. 963 We recommend that the description tag is used to document the extension 964 point. Pay special attention to the support (or lack of 965 support) for custom scripts, stylesheets and renderers. 966 </para> 967 </note> 968 969 <para> 970 The <sgmltag class="starttag">action-class</sgmltag> defines the 971 interface or class that extensions must provide to the extension 972 point. This must be a class or interface that is a subclass 973 of the <interfacename 974 docapi="net.sf.basedb.util.extensions">Action</interfacename> 975 interface. We generally recommend that interfaces are used since 976 this gives more implementation flexibility for action factories, but 977 a regular class may work just as well. 978 </para> 979 980 <para> 981 The action class is used to carry information about the action, 982 such as a title, which icon to use, a tooltip text, a javascript 983 snippet that is invoked on click events, etc. The action class may 984 be as simple or complex as you like. 985 </para> 986 987 <note> 988 <title>Web client extension points</title> 989 <para> 990 This is a note for the core developers. Extension points that 991 are part of the web client should always define the action as 992 an interface. We recommend that <code>getId()</code>, <code>getClazz()</code> 993 and <code>getStyle()</code> attributes are always included if this makes 994 sense. It is usually also a good idea to include <code>isVisible()</code> 995 and <code>isEnabled()</code> attributes. 996 </para> 997 </note> 998 999 <para> 1000 Now, if you are a good citizen you should also provide at least 1001 one implementation of an action factory that can create the 1002 objects of the desired type of action. The factory should 1003 of course be configurable from the XML file. 1004 </para> 1005 1006 <para> 1007 If you are lazy or if you want to immediately start testing the JSP code 1008 for the extension point, it may be possible to use one of the debugger action 1009 factories in the <package>net.sf.basedb.util.extensions.debug</package> 1010 pacakge. 1011 </para> 1012 1013 <itemizedlist> 1014 <listitem> 1015 <para> 1016 <classname 1017 docapi="net.sf.basedb.util.extensions.debug">ProxyActionFactory</classname>: 1018 This action factory can only be used if your action class is an interface 1019 and all important methods starts with <code>get</code> or <code>is</code>. 1020 The proxy action factory uses the Java reflection to create a dynamic 1021 proxy class in runtime. It will map all <code>get</code> and <code>is</code> 1022 methods to retreive the values from the corresponding parameter in the XML file. 1023 For example, <methodname>getIcon()</methodname> will retrieve the value 1024 of the <sgmltag class="starttag">icon</sgmltag> tag and 1025 <methodname>isVisible()</methodname> from the <sgmltag 1026 class="starttag">visible</sgmltag>. The factory is smart enough to convert 1027 the string to the correct return value for <type>int</type>, <type>long</type>, 1028 <type>float</type>, <type>double</type> and <type>boolean</type> data types and 1029 their corresponding object types, if this is needed. 1030 </para> 1031 </listitem> 1032 1033 <listitem> 1034 <para> 1035 <classname 1036 docapi="net.sf.basedb.util.extensions.debug">BeanActionFactory</classname>: 1037 This action factory can be used if you have created a bean-like 1038 class that implements the desired action class. The factory will 1039 create an instance of the class specified by the 1040 <sgmltag class="starttag">beanClass</sgmltag> parameter. The factory 1041 will then use Java reflection to find <code>set</code> method 1042 for the other parameters. If there is a parameter <sgmltag class="starttag">icon</sgmltag> 1043 the factory first looks for a <methodname>setIcon(String)</methodname> 1044 method. If it can't find that it will see if there is a <methodname>getIcon()</methodname> 1045 method which has a return type, T. If so, a second attempt is made to find 1046 a <methodname>setIcon(T)</methodname> method. The factory is smart enough 1047 to convert the string value from the XML file to the correct return value 1048 for <type>int</type>, <type>long</type>, <type>float</type>, 1049 <type>double</type> and <type>boolean</type> data types and their 1050 corresponding object types, if this is needed. 1051 </para> 1052 </listitem> 1053 1054 </itemizedlist> 1055 1056 1057 <para> 1058 It is finally time to write the JSP code that actually uses the 1059 extension point. It is usually not very complicated. Here is 1060 an exemple which lists snippets from a JSP page: 1061 </para> 1062 1063 <programlisting> 1064 <![CDATA[ 1065 // We recommend using the extensions taglib (and the BASE core taglib) 1066 <%@ taglib prefix="ext" uri="/WEB-INF/extensions.tld" %> 1067 <%@ taglib prefix="base" uri="/WEB-INF/base.tld" %> 1068 1069 // Prepare the extension point 1070 SessionControl sc = Base.getExistingSessionControl(pageContext, true); 1071 JspContext jspContext = ExtensionsControl.createContext(sc, pageContext); 1072 ExtensionsInvoker invoker = ExtensionsControl.useExtensions(jspContext, 1073 "my.domain.name.extensionspoint"); 1074 1075 // Output scripts and stylesheets 1076 <base:page title="My new extension point"> 1077 <base:head> 1078 <ext:scripts context="<%=jspContext%>" /> 1079 <ext:stylesheets context="<%=jspContext%>" /> 1080 </base:head> 1081 <base:body> 1082 .... 1083 1084 // Using a taglib for rendering with the default renderer 1085 <ext:render extensions="<%=invoker%>" context="<%=jspContext%>" /> 1086 1087 // Using an iterator 1088 <% 1089 Iterator it = invoker.iterate(); 1090 while (it.hasNext()) 1091 { 1092 MyAction action = (MyAction)it.next(); 1093 String html = action.getHtml(); 1094 out.write(html); 1095 } 1096 %> 1097 ]]> 1098 </programlisting> 1099 1100 </sect1> 1101 770 1102 </chapter>
Note: See TracChangeset
for help on using the changeset viewer.