Blogs : Archives
|
|
| < JavaPaste 1.0 released : pastebin with highlighting, diffing, private pastebins and image uploads | Nominated and accepted as Java Champion > |
|
Update: this announcement was posted on the front page of TheServerSide.com. If you have comments or are interested in what others have to say, it might be a good idea to head over there. Below are the highlights:
You can read the full changelog for more details. This release can be downloaded from the downloads section, as usual. Complete injection and outjection support for all element data (bijection)RIFE will now automatically detect setters and getters in your element implementation and map them to the inputs, outputs, parameters, inbeans, outbeans, incookies, outcookies, and file uploads that have been declared. Note that this is purely on a name basis and you're not required to use annotation for this to be supported. However, when you do use the annotations-based declaration (as explained below), your declarations are nicely bundled and you have no duplication of the names. Annotations support for element declarationAnnotations can now be used to create the element
declarations and kick in when you declare an element implementation
without an ID or file. All that is needed is the The following conventions are being used by default:
@Elem public class HelloWorld extends Element { public void processElement() { Template template = getHtmlTemplate("helloworld"); template.setValue("hello", "Hello world."); print(template); } }With the following site declaration: <site> <arrival destid="HelloWorld"/> <element implementation="HelloWorld"/> </site>The element will automatically
have the ID From here onwards,
you can start expanding the element declaration with annotations by adding
attributes to the If
you use a flowlink or a datalink with a To reduce the duplication of string literals for exit
names, you can use the For example: @Elem public class ProductListEntry extends Element { @FlowlinkExitField(destClass = EditProduct.class, destClassIdPrefix = "^Admin", datalinks = { @Datalink(srcOutput="productId", destInput="productId") }) public static final String EXIT_EDIT_PRODUCT = "editProduct"; @FlowlinkExitField(destClass = ShowProduct.class, datalinks = { @Datalink(srcOutput="productId", destInput="productId") }) public static final String EXIT_SHOW_PRODUCT = "showProduct"; private Product product; @OutputProperty public int getProductId() { if (product != null) return product.getId(); return -1; } public void processElement() { // ... your logic ... exit(EXIT_SHOW_PRODUCT); } }Instead of declaring inputs,
outputs, inbeans, outbeans, incookies, outcookies, submissions,
parameters, submission beans and files inside the Note that for the property declaration of parameters,
files, and beans for a submission, RIFE will add them to the last declared
submission. However, Java doesn't guarantee the order of methods in a
class. That's why you need to use the For example: @Elem public class MyElement extends Element { private String param1; private String param2; private String param3; private UploadedFile file1; private UploadedFile file2; @Priority({1}) @SubmissionHandler public void doMySubmission() { // ... } @Priority({1, 1}) @ParamProperty public void setParam1(String param1) { this.param1 = param1; } @Priority({1, 1}) @FileProperty public void setFile1(UploadedFile file1) { this.file1 = file1; } @Priority({2}) @SubmissionHandler public void doAnotherSubmission() { // ... } @Priority({2, 1}) @ParamProperty public void setParam2(String param2) { this.param2 = param2; } @Priority({2, 1}) @ParamProperty public void setParam3(String param3) { this.param3 = param3; } @Priority({2, 1}) @FileProperty public void setFile2(UploadedFile file2) { this.file2 = file2; } public void processElement() { // ... } }The code above declares the
submission ' It's important to note that any method that doesn't
contain a Support for parallel and simultaneous continuationsContinuations can run in embedded elements and any number of them can be active at the same time. Below is the implementation of a page with multiple counters that all run independently as continuations with while loops. The
@Elem(url="", submissions = { @Submission(name = "decrease"), @Submission(name = "increase")}) public class Counter extends Element { public void processElement() { int counter = 0; Template t = getHtmlTemplate("counter"); while (true) { t.setValue("counter", counter); print(t); pause(); if (hasSubmission("decrease")) counter--; if (hasSubmission("increase")) counter++; } } }The <div> <r:v name="counter"/> <form action="${v SUBMISSION:FORM:decrease/}" method="post"> <r:v name="SUBMISSION:PARAMS:decrease"/> <input type="submit" value=" - " /> </form> <form action="${v SUBMISSION:FORM:increase/}" method="post"> <r:v name="SUBMISSION:PARAMS:increase"/> <input type="submit" value=" + " /> </form> </div>The main template that includes three counters as embedded elements: <body> <r:v name="ELEMENT:Counter:1"/> <br /> <r:v name="ELEMENT:Counter:2"/> <br /> <r:v name="ELEMENT:Counter:3"/> </body>Fine-grained control over continuation trees and their invalidationContinuations are perfect for expressing a transactional multi-step process that either fully completes, or not at all. At completion however, you most of the time have the requirement that the same process should not be able to be completed a second time (when a user re-submits the final form, for example a payment, this should not be accepted). It's now very easy to achieve this by using continuations by simply removing the entire continuation tree. This means that when the user re-submits, the corresponding continuation will not be found and instead of resuming at a previously paused location, the execution will jump back all the way to the initial step of the multi-step process. The get access to the active continuation context and remove the associated continuation tree, you can use this simple call: ContinuationContext.getActiveContext().removeContextTree();Additional useful methods are available in the ContinuationContext class and allow you to introspect and manipulate the continuation hierarchy. Step-back continuationsContinuations have always been used with a 'forward thinking' approach. With that I mean that when you resume after you pause, the next code is executed using the same local variable state. This results in explanations like: you continue where you left off, or continuations contain the remaining work to be done, ... With this release of RIFE, however, we allow you to step back to a previous location in the code. Instead of resuming where you left off when you paused, you can now also resume where you left off in the previous continuation. The
availability of this features makes it extremely easy to add 'back' submit
buttons to multi-step flows. You just have to detect that it has been
clicked and execute the For example (you can try out an online version of this example on the rifers.org website):
// handle the submission of the shipping details do { generateForm(template, order); template.setBlock("content_form", "content_shipping"); print(template); pause(); template.clear(); order.resetValidation(); fillSubmissionBean(order); } while (duringStepBack() || !order.validateGroup("shipping")); // handle the submission of the credit card details do { generateForm(template, order); template.setBlock("content_form", "content_creditcard"); print(template); pause(); template.clear(); order.resetValidation(); fillSubmissionBean(order); // if the form input button with the name "back1" was // pressed, a step-back continuation is executed, // this makes the flow jump to first pause() call in // this code snippet (not the pause call inside this // while loop) if (hasParameterValue("back1")) stepBack(); } while (!order.validateGroup("creditcard")); // provide an overview of everything that has been submitted template.setBean(order); template.setBlock("content", "content_overview"); print(template); // remove any continuation contexts that are active in this tree ContinuationContext.getActiveContext().removeContextTree();Note that the first loop
contains an additional check: " A very nice feature of this setup is that the data of the second step (the credit card details) is filled into the bean but not validated when the back button is pressed. Users can thus fill in incomplete data, go to the previous step in the wizard without being interrupted, change the data in the first form, and after submission they will see the same incomplete data that they entered before stepping back. Stateful componentsRIFE now makes it very easy for element instances to indicate which state needs to be preserved for them (in case you don't want to use continuations for this). You simply create datalinks that point back to exactly the same element as the one they originate from. These are called reflexive datalinks. The data that's available through the connected outputs will be collected by RIFE and provided as inputs to the element when it's processed the next time. Note, this seperates out the collected data for each individual element instance you use on a site, even for embedded elements. While this is the behavior you would expect, it's worth pointing out that the data is not preserved globally for all the elements of the same type. Let's look at the same counter example that we used to demonstrate parallel continuations, only this time it uses stateful components (you can try this version out online). The element implementation
@Elem( // Setting an empty URL, makes the submissions target the element that embeds // the embedded Counter elements. Without this declaration, each embedded // element would become the main element after the submission (since it has // its own URL). url="", // This data link connects the 'counter' output to the 'counter' value. The // element might have changed the counter property value in the meantime // (after an 'increase' or 'decrease' submission). The reflective datalink // will pass the output value to the input at the next submission. datalinks = {@Datalink(srcOutput="counter", destInput="counter", destClass=Counter.class)} ) public class Counter extends Element { private int counter; @InputProperty public void setCounter(int counter) { this.counter = counter; } @OutputProperty public int getCounter() { return counter; } @SubmissionHandler public void doDecrease() { counter--; processElement(); } @SubmissionHandler public void doIncrease() { counter++; processElement(); } public void processElement() { Template t = getHtmlTemplate("counter"); t.setValue("counter", counter); print(t); } }The counter.html template that is used by the element above: <div> <r:v name="counter"/> <form action="${v SUBMISSION:FORM:decrease/}" method="post"> <r:v name="SUBMISSION:PARAMS:decrease"/> <input type="submit" value=" - " /> </form> <form action="${v SUBMISSION:FORM:increase/}" method="post"> <r:v name="SUBMISSION:PARAMS:increase"/> <input type="submit" value=" + " /> </form> </div>The main template that includes three stateful counters as embedded elements: <body> <r:v name="ELEMENT:Counter:1"/> <br /> <r:v name="ELEMENT:Counter:2"/> <br /> <r:v name="ELEMENT:Counter:3"/> </body>Support for creating RIFE applications without any XMLThe last part of the architecture of a
RIFE web application that still required XML, was the repository setup.
You needed to provide a For example: <web-app> <filter> <filter-name>RIFE</filter-name> <filter-class>com.uwyn.rife.servlet.RifeFilter</filter-class> <init-param> <param-name>lifecycle.classname</param-name> <param-value>LifeCycle</param-value> </init-param> </filter> <filter-mapping> <filter-name>RIFE</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> Then you can create your own LifeCycle class like this: public class LifeCycle extends RifeLifecycle { public LifeCycle() { BlockingRepository rep = new BlockingRepository(); rep.addParticipant(ParticipantSite.class); rep.runParticipants(); Rep.setDefaultRepository(rep); } }Don't forget to set the default repository in your custom lifecycle, since much of RIFE relies on the presence of that. Performance improvementsA lot of work has been done to improve the performance both for development as for production. These two are the most noteworthy:
Support for reloading manually declared sites by site listenersWhen you create a site through the
Java API of the For example, this is a custom site participant that does just that:
public class MySiteParticipant extends BlockingParticipant { private Site site; public void initialize() { SiteBuilder builder = new SiteBuilder("main"); builder .enterElement("YourElement") // ... .leaveElement() // ... other elements and sub sites ; site = builder.getSite(); site.addListener(new SiteListener() { public void modified(Site modifiedSite) { initialize(); modifiedSite.populateFromOther(site); } }); } protected Object _getObject() { return site; } }Automatic recompilation of non-hotswappable or instrumented classesRIFE will now always
recompile a changed Java implementation of an element if the source is
available through the classpath, the Note that you have to be careful about your application classpath setup here. You have two options:
In either case, you have to take extra care if you setup an
application structure yourself outside the traditional
Generic Query Manager listenersCallbacks
have been available since RIFE version 1.0. We now however also introduced
Full changelog2006-07-13 Geert Bevin <gbevin[remove] at uwyn dot com> * RELEASE 1.5 2006-07-12 Geert Bevin <gbevin[remove] at uwyn dot com> * Updated mime type property for the examples highlighted source code. * Updated changelog 2006-07-11 Geert Bevin <gbevin[remove] at uwyn dot com> * Placed empty target types on annotations that can only be used nested inside enclosing annotations. * RIFE-284 : @Exit annotation should be valid on fields * Ant build file fixes 2006-07-10 Geert Bevin <gbevin[remove] at uwyn dot com> * Minor refactorings concerning state store extensibility * Generated highlighted sources of the latest version of the examples. * Example refactoring and source code re-formatting * Changed the dominant template tag syntax in the examples into <r:v name=""/> * Very rare NPE fix. * RIFE-291 : Annotation destClass attributes should also support a destClassIdPrefix attribute 2006-07-09 Geert Bevin <gbevin[remove] at uwyn dot com> * Compilation fixes for windows and environment setup fixes for windows. * Tmp path fixes * Updated project files. * RIFE-281 : INVALID should suppress/clear MANDATORY message 2006-07-08 Geert Bevin <gbevin[remove] at |


