[Yanel-commits] rev 47362 - in
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources:
. jellyadapterofcmdv3
michi at wyona.com
michi at wyona.com
Sat Jan 30 15:57:02 CET 2010
Author: michi
Date: 2010-01-30 15:57:02 +0100 (Sat, 30 Jan 2010)
New Revision: 47362
Added:
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/BasicFormResource.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/Continuation.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ControllerAdapter.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ConversationState.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyAdapterForCUDResource.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyControllerAdapter.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyConversationAdapter.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ResourceAdapter.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ViewDescriptorUsingTemplate.java
Removed:
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java
public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java
Log:
classes moved into unique package name
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,324 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map.Entry;
-
-import org.apache.log4j.Logger;
-import org.wyona.yanel.core.ResourceNotFoundException;
-import org.wyona.yanel.core.attributes.viewable.View;
-
-import javax.mail.Message;
-import javax.mail.Session;
-import javax.mail.Transport;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-
-/**
- * Base class to handle forms. Returns the following XML streams:
- *
- * <p>Initial call:</p>
- * <pre>
- * <form-resource>
- * <form/>
- * </form-resource>
- * </pre>
- *
- * <p>Validation failed:</p>
- * <pre>
- * <form-resource>
- * <form/>
- * <validation/>
- * <input/>
- * <result/>
- * </form-resource>
- * </pre>
- *
- * <p>Successful processing:</p>
- * <pre>
- * <form-resource>
- * <confirmation/>
- * <input/>
- * <result/>
- * </form-resource>
- * </pre>
- *
- * <p>Error occurred:</p>
- * <pre>
- * <form-resource>
- * <error/>
- * <input/>
- * <result/>
- * </form-resource>
- * </pre>
- *
- * @author Matthias Leumann
- *
- */
-public abstract class BasicFormResource extends BasicXMLResource {
-
- private static final Logger log = Logger.getLogger(BasicFormResource.class);
-
- private static final String ENCODING = "UTF-8";
- private static final String INPUT_SUBMIT = "submit";
-
- protected static final String RESULT_CODE_DONE_SUCCESSFUL = "done-successful";
- private static final String RESULT_CODE_FAILED_INVALID = "failed-invalid";
- private static final String RESULT_CODE_FAILED_ERROR = "failed-error";
-
- private String resultCode;
- private StringBuilder resultXml = new StringBuilder();
- private LinkedList<NameValuePair> validationErrors = new LinkedList<NameValuePair>();
-
- public View getView(String viewId) throws Exception {
- String sslRedirectParam = getResourceConfigProperty("ssl-redirect");
- if (sslRedirectParam != null && sslRedirectParam.equals("true")) {
- if (!getEnvironment().getRequest().isSecure() && getEnvironment().getRequest().getParameter("ssl") == null) {
- View view = new View();
- view.setResponse(false);
- URL sslURL;
- int sslPort = 443;
- if (getRealm().isProxySet()) {
- if (realm.getProxySSLPort() >= 0) sslPort = realm.getProxySSLPort();
- String requestURI = getEnvironment().getRequest().getRequestURI();
- if (realm.getProxyPrefix() != null) requestURI = requestURI.substring(realm.getProxyPrefix().length());
- sslURL = new URL("https", getRealm().getProxyHostName(), sslPort, requestURI + "?ssl=true");
- } else {
- sslURL = new URL("https", getEnvironment().getRequest().getServerName(), sslPort, getEnvironment().getRequest().getRequestURI() + "?ssl=true");
- }
- log.warn("Redirect to SSL: " + sslURL.toString());
- getEnvironment().getResponse().setHeader("Location", sslURL.toString());
- getEnvironment().getResponse().setStatus(javax.servlet.http.HttpServletResponse.SC_TEMPORARY_REDIRECT);
- return view;
- }
- }
- return super.getView(viewId);
- }
-
- protected final InputStream getContentXML(String viewId) throws Exception {
- if (!exists()) {
- log.warn("No such resource: " + getPath());
- throw new ResourceNotFoundException("No such resource: " + getPath());
- }
-
- resultXml.append("<form-resource xmlns=\""+getNamespaceURI()+"\">");
- if (isFormSubmitted()) {
- validate();
- if (isInputValid()) {
- try {
- resultCode = execute();
- appendConfirmation();
- } catch (Exception e) {
- log.error(e, e);
- resultCode = RESULT_CODE_FAILED_ERROR;
- appendError(e);
- }
- } else {
- resultCode = RESULT_CODE_FAILED_INVALID;
- appendForm();
- appendValidation();
- }
- appendInput();
- appendResult();
- } else {
- appendForm();
- }
- resultXml.append("</form-resource>");
- return new ByteArrayInputStream(resultXml.toString().getBytes(ENCODING));
- }
-
- /**
- * Adds a validation error. Use this method to report invalid input.
- *
- * @param inputName name of the input parameter
- * @param validationMessage validation message (may use i18n key)
- */
- protected final void addValidationError(String inputName, String validationMessage) {
- validationErrors.add(new NameValuePair(inputName, validationMessage));
- }
-
- /**
- * @deprecated Use org.wyona.yanel.core.util.MailUtil.send() instead
- *
- * Sends an email. E.g. use this method to send confirmation mails.
- *
- * @param from sender email address
- * @param replyTo email address (if null, then no reply-to will be set)
- * @param to receiver email address
- * @param subject email subject
- * @param content email content
- */
- protected void sendMail(String from, String replyTo, String to, String subject, String content) throws Exception {
- log.warn("DEPRECATED");
- org.wyona.yanel.core.util.MailUtil.send(from, replyTo, to, subject, content);
-
-
-/*
- // Create a mail session
- java.util.Properties props = new java.util.Properties();
- props.put("mail.smtp.host", getResourceConfigProperty("smtpHost"));
- props.put("mail.smtp.port", "" + getResourceConfigProperty("smtpPort"));
- // TODO: http://java.sun.com/products/javamail/javadocs/javax/mail/Session.html
- Session session = Session.getDefaultInstance(props, null);
-
- // Construct the message
- Message msg = new MimeMessage(session);
- msg.setFrom(new InternetAddress(from));
- if (replyTo != null) {
- InternetAddress[] replyToAddresses = new InternetAddress[1];
- replyToAddresses[0] = new InternetAddress(replyTo);
- msg.setReplyTo(replyToAddresses);
- }
- if( log.isDebugEnabled() )
- log.debug("From: " + msg.getFrom() + ", " + from);
- msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
- msg.setSubject(subject);
- msg.setText(content);
-*/
-
-/*
- msg.setSubject(subject,"utf-8");
- msg.setContent(content, "text/plain;charset=utf-8");
-*/
-
-/*
- // Send the message
- Transport.send(msg);
-*/
- }
-
- /**
- * Generates valid XML containing the data to display the form screen.
- *
- * @return valid XML string
- */
- protected abstract String generateForm() throws Exception;
-
- /**
- * Generates valid XML containing the data to display the confirmation screen.
- *
- * @return valid XML string
- */
- protected abstract String generateConfirmation() throws Exception;
-
- /**
- * Validates the form input. Use the method <code>addValidationError</code> to report invalid input.
- */
- protected abstract void validate() throws Exception;
-
- /**
- * Executes custom operation if the form input was successfully validated.
- *
- * @return result code (e.g. "done-successful")
- * @throws Exception if processing failed.
- */
- protected abstract String execute() throws Exception;
-
- /**
- * @return the name space URI of of the resulting XML.
- */
- protected abstract String getNamespaceURI();
-
-
- /**
- * @return returns true if and only if the input parameters contain a parameter "submit".
- */
- private boolean isFormSubmitted() {
- return getParameterAsString(INPUT_SUBMIT) != null;
- }
-
- /**
- * @return true if and only if there are no validation errors.
- */
- private boolean isInputValid() {
- return validationErrors.isEmpty();
- }
-
- /**
- * Appends the result code to the result XML.
- */
- private void appendResult() {
- resultXml.append("<result>");
- resultXml.append(resultCode);
- resultXml.append("</result>");
- }
-
- /**
- * Appends the data for the form screen to the result XML.
- */
- private void appendForm() throws Exception {
- resultXml.append("<form>");
- String form = generateForm();
- if (form != null) {
- resultXml.append(form);
- }
- resultXml.append("</form>");
- }
-
- /**
- * Appends the data for the confirmation screen to the result XML.
- */
- private void appendConfirmation() throws Exception {
- resultXml.append("<confirmation>");
- String confirmation = generateConfirmation();
- if (confirmation != null) {
- resultXml.append(confirmation);
- }
- resultXml.append("</confirmation>");
- }
-
- /**
- * Appends the validation messages to the result XML.
- */
- private void appendValidation() {
- resultXml.append("<validation>");
- for (NameValuePair error : validationErrors) {
- resultXml.append("<"+error.getName()+">"+error.getValue()+"</"+error.getName()+">");
- }
- resultXml.append("</validation>");
- }
-
- /**
- * Appends the form input to the result XML.
- */
- private void appendInput() {
- resultXml.append("<input>");
- Iterator it = getParameters().entrySet().iterator();
- while (it.hasNext()) {
- Entry param = (Entry)it.next();
- resultXml.append("<"+param.getKey()+"><![CDATA["+param.getValue()+"]]></"+param.getKey()+">");
- }
- resultXml.append("</input>");
- }
-
- private void appendError(Exception e) {
- resultXml.append("<error>");
- resultXml.append("<![CDATA[" + e.toString() + "]]>");
- resultXml.append("</error>");
- }
-
- /**
- * Private utility class to add validation errors to the list.
- */
- private class NameValuePair {
- private String name;
- private String value;
-
- public NameValuePair(String name, String value) {
- this.name = name;
- this.value = value;
- }
-
- public String getName() {
- return name;
- }
-
- public String getValue() {
- return value;
- }
- }
-
-}
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,31 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-/**
- *
- */
-public class Continuation {
-
- private String id;
-
- /**
- *
- */
- public Continuation() {
- // TODO: Use UUID
- id = "" + new java.util.Date().getTime();
- }
-
- /**
- *
- */
- public Continuation(String id) {
- this.id = id;
- }
-
- /**
- *
- */
- public String getId() {
- return id;
- }
-}
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,156 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.apache.log4j.Logger;
-import org.wyona.yanel.core.Constants;
-import org.wyona.yanel.core.Resource;
-import org.wyona.yanel.core.api.attributes.ViewableV2;
-import org.wyona.yanel.core.attributes.viewable.ViewDescriptor;
-
-abstract class ControllerAdapter extends Resource implements ResourceAdapter, ViewableV2 {
- private static Logger log = Logger.getLogger(ControllerAdapter.class);
- public static String YANEL_CONTINUATION_ID = "yanel.continuation.id";
-
- /**
- * Resource config property for internationalization
- * */
- //TODO: what is that???
- private static final String I18N_CATALOG_PROPERTY = "i18n-catalogue";
-
- protected final Set<String> supportedViewIds = new HashSet<String>();
-
- private String adaptedResourcePath = null;
-
- private Usecase usecase = null;
-
- /**
- * Constants.Request.DEFAULT_VIEW_ID is registered
- */
- public ControllerAdapter() {
- supportedViewIds.add(Constants.Request.DEFAULT_VIEW_ID);
- }
-
- /**
- * Checks if the path was set, otherwise
- * looks into available parameters and returns the value of
- * PARAM_ADAPTED_RESOURCE_PATH
- * @return null when the parameter is not found
- * */
- public final String getAdaptedResourcePath() {
- if(adaptedResourcePath == null){
- adaptedResourcePath = getParameterAsString(PARAM_ADAPTED_RESOURCE_PATH);
- }
- return adaptedResourcePath;
- }
-
- public void setAdaptedResourcePath(String adaptedResourcePath) {
- this.adaptedResourcePath = adaptedResourcePath;
- }
-
- /**
- * Checks if the usecase was set, otherwise looks into available parameters
- * @return null when usecase can't be determined
- * */
- public final Usecase getUsecase() {
- if(usecase == null){
- String u = getParameterAsString(Constants.Request.YANEL_RESOURCE_USECASE);
- if (u == null) {
- try {
- u = getResourceConfigProperty("usecase");
- } catch (Exception e) {
- log.error(e, e);
- }
- if (u == null) {
- log.error("No usecase (neither 'create' nor 'update/modify' nor 'remove/delete') has been specified (neither within conversation nor query string nor resource config)!");
- return null;
- }
- }
- usecase = Usecase.caseInsensitiveValueOf(u);
- }
- return usecase;
- }
-
- public void setUsecase(Usecase usecase) {
- this.usecase = usecase;
- }
-
- /**
- * Get view descriptor
- * @param viewId For example 'video'
- */
- public ViewDescriptor getViewDescriptor(String viewId) {
- ViewDescriptor[] viewDescriptors = getViewDescriptors();
- for (int i = 0; i < viewDescriptors.length; i++) {
- if (viewDescriptors[i].getId().equalsIgnoreCase(viewId)) {
- return viewDescriptors[i];
- }
- }
- return null;
- }
-
- /**
- * Checks for validity of the viewId. For instance,
- * "CANCEL" is the same as "cancel". So this method normalizes
- * the view id.
- * <p>
- * When the client is doing some logic for some view, it first should normalize it with this method
- *
- * @return the normalized viewId (lowercase), when the viewId is <code>null</code> it returns the default view id
- * */
- protected final String normalize(String viewId) {
- if(viewId == null || "".equals(viewId.trim())){
- return Constants.Request.DEFAULT_VIEW_ID;
- }
- for (Iterator i = supportedViewIds.iterator(); i.hasNext();) {
- String id = (String) i.next();
- if(id.equalsIgnoreCase(viewId)){
- return id;
- }
- }
- return viewId.toLowerCase();
- }
-
- /**
- * Gets the names of the i18n message catalogues used for the i18n transformation.
- * Looks for an rc config property named 'i18n-catalogue'. Defaults to 'global'.
- * @return i18n catalogue name
- */
- protected String[] getI18NCatalogueNames() throws Exception {
- String[] catalogueNames = getResourceConfigProperties(I18N_CATALOG_PROPERTY);
- if (catalogueNames == null || catalogueNames.length == 0) {
- catalogueNames = new String[1];
- catalogueNames[0] = "global";
- }
- return catalogueNames;
- }
-
- public long getSize() throws Exception {
- return -1;
- }
-
- public boolean exists() throws Exception {
- return true;
- }
-
- /**
- * Returns the current continuation associated with the request which is associated with this resource, or if the request does not have a continuation, creates one.
- */
- public Continuation getContinuation() {
- if (getRequest().getParameter(YANEL_CONTINUATION_ID) != null) {
- String continuationId = getRequest().getParameter(YANEL_CONTINUATION_ID);
- log.warn("Return existing continuation: " + continuationId);
- return new Continuation(continuationId);
- //return new ContinuationSessionImpl(continuationId, getSession(true));
- //return new ContinuationYarepRepoImpl(continuationId, continuationRepo);
- } else {
- Continuation continuation = new Continuation();
- //Continuation continuation = new ContinuationSessionImpl(getSession(true));
- //Continuation continuation = new ContinuationYarepRepoImpl(continuationRepo);
- log.warn("New continuation created: " + continuation.getId());
- return continuation;
- }
- }
-}
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,108 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.io.Serializable;
-
-import org.wyona.yanel.impl.resources.ResourceAdapter.Usecase;
-
-/**
- * Conversation state is stored in the session.
- * During the conversation with the user the model is modified.
- * However, the model is transient.
- */
-class ConversationState implements Serializable{
- private static final long serialVersionUID = 1L;
-
- //private static final long serialVersionUID = 1724827714145174641L;
- private Usecase usecase;
-
- /**
- * The path to the adapted resource, e.g. Creatable, Modifiable
- */
- private String resourcePath;
- private String refererUrl;
- private String gotoUrl;
- private String currentViewId;
- private String previousViewId;
- private transient Object model;
- private boolean invalidated;
-
- public ConversationState(Object model){
- this.model = model;
- }
-
- public ConversationState(String currentViewId, Object model){
- this.currentViewId = currentViewId;
- this.model = model;
- }
-
- public String getCurrentScreen() {
- return currentViewId;
- }
-
- public String getPreviousScreen() {
- return previousViewId;
- }
-
- public Object getModel() {
- return model;
- }
-
- public String getRefererUrl() {
- return refererUrl;
- }
-
- void setRefererUrl(String refererUrl) {
- this.refererUrl = refererUrl;
- if(gotoUrl == null){
- this.gotoUrl = this.refererUrl;
- }
- }
-
- public String getGotoUrl() {
- return gotoUrl;
- }
-
- public void setGotoUrl(String gotoUrl) {
- this.gotoUrl = gotoUrl;
- }
-
- /**
- * Sets the previous and current view id
- * */
- void setCurrentScreen(String currentViewId) {
- this.previousViewId = this.currentViewId;
- this.currentViewId = currentViewId;
- }
-
- void setModel(Object model) {
- this.model = model;
- }
-
- void invalidate(){
- invalidated = true;
- }
-
- /**
- * Invalidated conversation state means that the state is removed from
- * the session
- * */
- public boolean isInvalidated(){
- return invalidated;
- }
-
- public Usecase getUsecase() {
- return usecase;
- }
-
- public void setUsecase(Usecase usecase) {
- this.usecase = usecase;
- }
-
- public String getResourcePath() {
- return resourcePath;
- }
-
- public void setResourcePath(String resourcePath) {
- this.resourcePath = resourcePath;
- }
-}
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,451 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.transform.Transformer;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.jelly.JellyContext;
-import org.wyona.yanel.core.Resource;
-import org.wyona.yanel.core.api.attributes.CreatableV3;
-import org.wyona.yanel.core.api.attributes.DeletableV1;
-import org.wyona.yanel.core.api.attributes.ModifiableV3;
-import org.wyona.yanel.core.api.attributes.creatable.ResourceInput;
-import org.wyona.yanel.core.api.attributes.creatable.ResourceInputItem;
-import org.wyona.yanel.core.api.attributes.creatable.ResourceInputItemCollection;
-import org.wyona.yanel.core.attributes.viewable.View;
-import org.wyona.yanel.core.util.ResourceAttributeHelper;
-import org.wyona.yanel.impl.jelly.FileItem;
-import org.wyona.yanel.servlet.communication.HttpRequest;
-
-import org.apache.log4j.Logger;
-
-/**
- * Adapts a CreatableV3.
- * <p>
- * The templates in addition get the following parameters:
- * <ul>
- * <li>resourceInput - description of the inputs that creatable resource needs
- * <li>url.before.conversation - the VIEW can redirect here when the conversation is cancelled
- * <li>resource.input.collection.action - Decides on what kind of action is taken on collection items. Can be 'add' (default) or 'remove'.
- * </ul>
- *
- * This is quite a generic class. Subclasses may want to pass other parameters to the templates.
- * */
-public class JellyAdapterForCUDResource extends JellyConversationAdapter {
-
- private static Logger log = Logger.getLogger(JellyAdapterForCUDResource.class);
-
- // Collection items are special. Values can be removed or added/set to specific index
- public static final String REMOVE_ACTION = "remove";
- public static final String PARAM_RESOURCE_INPUT_COLLECTION_ACTION = "resource.input.collection.action";
- public static final String PARAM_RESOURCE_INPUT_COLLECTION_INDEX = "resource.input.collection.index";
-
- // The three parameters must be specified together!
- public static final String PARAM_RESOURCE_INPUT_COLLECTION_OLD_INDEX = "resource.input.collection.old.index";
- public static final String PARAM_RESOURCE_INPUT_COLLECTION_NEW_INDEX = "resource.input.collection.new.index";
- public static final String PARAM_TARGET_RESOURCE_INPUT = "target.resource.input";
-
- public JellyAdapterForCUDResource() {
- }
-
- /**
- *
- */
- public View getView(String viewId)throws Exception{
- try{
- ViewDescriptorUsingTemplate viewDescriptor = (ViewDescriptorUsingTemplate)getViewDescriptor(normalize(viewId));
- if(viewDescriptor == null){
- throw new IllegalArgumentException("The view descriptor was not found for the given id: '" + viewId + "'");
- }
- log.debug("View ID: " + viewDescriptor.getId());
-
- if (CANCEL_VIEW_ID.equals(viewDescriptor.getId())) {
- log.warn("Cancel ...");
- doCancel();
- destroyConversation();
- return super.getView(CANCEL_VIEW_ID);
- }
-
- // Deal with the input
- init();
- ConversationState cs = getConversationState();
-
- boolean incomingInputIsValid = true;
- if (cs == null) {
- throw new IllegalStateException("Conversation state is not available");
- }
- String oldIndex = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_OLD_INDEX);
- String newIndex = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_NEW_INDEX);
- String [] targetInputs = getParameterAsStringValues(PARAM_TARGET_RESOURCE_INPUT);
-
- if(oldIndex != null && newIndex != null){
- try {
- int o = Integer.parseInt(oldIndex);
- int n = Integer.parseInt(newIndex);
-
- ResourceInput ri = (ResourceInput)cs.getModel();
-
- // Moving input values
- for (String target : targetInputs) {
- ResourceInputItem item = ri.getItem(target);
- if (item instanceof ResourceInputItemCollection) {
- ResourceInputItemCollection itemAsCollection = (ResourceInputItemCollection) item;
- itemAsCollection.moveValue(o, n);
- }
- }
- } catch (NumberFormatException e) {
- log.warn("oldIndex="+oldIndex+", newIndex="+newIndex+": Illegal indexes passed. Operation skiped");
- }
- }else{
- // Fill incoming values and validate. This will also
- incomingInputIsValid = fillIncomingInput(cs);
- }
-
- String nextViewId = viewDescriptor.getId();
-
-
- if(!incomingInputIsValid){
- if(!viewDescriptor.isFragment()){
- // When the screen was not valid go to the previous screen
- nextViewId = cs.getPreviousScreen();
- }else{
- }
- // Keep the same screen for the fragment
- cs.setCurrentScreen(cs.getPreviousScreen());
- }else {
- if(!viewDescriptor.isFragment()){
- // Now the current screen is the next view
- String previous = cs.getPreviousScreen();
- cs.setCurrentScreen(nextViewId);
- if (DONE_VIEW_ID.equals(nextViewId)) {
- // NOTE: assumes that DONE_VIEW_ID is not a fragment
- try {
- commit();
- destroyConversation();
- } catch (IllegalArgumentException e) {
- log.warn("Cannot commit", e);
- //thrown when the ResourceInput is not valid, then go back
- nextViewId = previous;
- cs.setCurrentScreen(previous);
- }
- }
- }else{
- // Keep the previous screen when the input is a fragment
- cs.setCurrentScreen(cs.getPreviousScreen());
- }
- }
- return super.getView(nextViewId);
- } catch(Throwable e){
- // Cancel the conversation when something goes wrong
- destroyConversation();
-
- throw new Exception("Due to an exception the request has been canceled. Exception message: " + e.getMessage(), e);
- }
- }
-
- /**
- * Try to set the resource input values according to what came in HTTP
- * request.
- * <p>
- * NOTE[very important]: this method relies on the fact that HTTP request
- * has parameters even for checkboxes and select(multiple), which are normally not submitted
- * when nothing is selected or checked. The "nothing is selected" means that the
- * parameter is passed but the value is <code>null</code>.
- * This must be ensured in order to use the adapter correctly, e.g. the simple tag library is
- * rendering an additional HIDDEN field for this purpose.
- * @return true when all the incoming input items are valid
- */
- private boolean fillIncomingInput(ConversationState cs) throws Exception {
- ResourceInput ri = (ResourceInput) cs.getModel();
- String[] itemNames = ri.getItemNames();
-
- Set incomingParameterNames = new HashSet(getParameters().keySet());
- // Add incoming names of the inputs that are files
- if (getEnvironment().getRequest() instanceof HttpRequest) {
- HttpRequest httpRequest = (HttpRequest) request;
- if (httpRequest.isMultipartRequest()) {
- Enumeration params = httpRequest.getFileNames();
- while (params.hasMoreElements()) {
- incomingParameterNames.add(params.nextElement());
- }
- }
- }
- //Now we have all incoming parameter names
- boolean inputValid = true;
- for (int i = 0; i < itemNames.length; i++) {
- String action = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_ACTION);
- String index = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_INDEX);
-
- if(!incomingParameterNames.contains(itemNames[i])){
- // We need to fill only the parameters that were passed
- continue;
- }
-
- // Go further to addValue()/removeValue()/setValue()
-
- Object paramValue = getParameterAsFileItem(itemNames[i]);
- if(paramValue == null){
- paramValue = getParameter(itemNames[i]);
- }
-
- if (REMOVE_ACTION.equals(action)){
- if (ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_COLLECTION) {
- ResourceInputItemCollection collectionItem = (ResourceInputItemCollection) ri.getItem(itemNames[i]);
-
- // Remove the value (maybe at the specified index)
- boolean removed = false;
- if(paramValue != null){
- removed = collectionItem.removeValue(paramValue);
- }
- if(!removed && index != null){
- try{
- int valueIndex = Integer.parseInt(index);
- collectionItem.removeValue(valueIndex);
- }catch(NumberFormatException e){
- log.warn(itemNames[i]+"="+paramValue+": Could not remove the parameter at the index "+index);
- }
- }
- }else{
- ri.getItem(itemNames[i]).setValue(null);
- }
- }else{
- if(ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_FILE_UPLOAD){
- FileItem fi = (FileItem)paramValue;
- if(fi == null || !fi.hasData()){
- // This will cause the old data (maybe null) to be left and validated
- inputValid = ri.getItem(itemNames[i]).validate() && inputValid;
- continue;
- }
- }
-
- if (ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_COLLECTION) {
- ResourceInputItemCollection collectionItem = (ResourceInputItemCollection) ri.getItem(itemNames[i]);
- if(index != null){
- try{
- int valueIndex = Integer.parseInt(index);
- collectionItem.addValue(valueIndex, paramValue);
- }catch(NumberFormatException e){
- log.warn(itemNames[i]+"="+paramValue+": Could not add the parameter at the index "+index+", appending");
- collectionItem.addValue(paramValue);
- }
- }else{
- collectionItem.addValue(paramValue);
- }
- } else {
- ri.getItem(itemNames[i]).setValue(paramValue);
- }
- }
- inputValid = ri.getItem(itemNames[i]).validate() && inputValid;
- }
-
- return inputValid;
- }
-
- /**
- * @return <code>null</code> when the parameter is not a file upload, otherwise a constructed file item.
- * */
- private FileItem getParameterAsFileItem(String parameterName)throws Exception{
- FileItem fileItem = null;
- if (getEnvironment().getRequest() instanceof HttpRequest) {
- HttpRequest httpRequest = (HttpRequest) request;
- if (httpRequest.isMultipartRequest()) {
- Enumeration fileParameterNames = httpRequest.getFileNames();
- while (fileParameterNames.hasMoreElements()) {
- // create new file item
- String name = (String) fileParameterNames.nextElement();
- if (name.equals(parameterName)) {
- String contentType = httpRequest.getContentType(name);
- InputStream is = httpRequest.getInputStream(name);
- fileItem = new FileItem(IOUtils.toByteArray(is), contentType);
- fileItem.setFileName(httpRequest.getFilesystemName(name));
- break;
- }
- }
- }
- }
- return fileItem;
- }
-
- /**
- *
- */
- protected void init() throws Exception {
- ConversationState cs = getConversationState();
-
- // try to identify the usecase. It is either in the request or in the conversation
- Usecase usecase = getUsecase();
- if (usecase == null) {
- log.warn("No usecase (neither 'create' nor 'update' nor 'delete') has been specified!");
- //throw new UnsupportedOperationException("No usecase (neither 'create' nor 'update' nor 'delete') has been specified!");
- }
- List<Usecase> supportedUsecases = Arrays.asList(Usecase.values());
- if (!supportedUsecases.contains(usecase)) {
- if (cs == null || !supportedUsecases.contains(cs.getUsecase())){
- throw new UnsupportedOperationException("The following usecase is not supported by this adapter implementation: " + usecase);
- }
- usecase = cs.getUsecase();
- }
-
- String resourcePath = getAdaptedResourcePathFromConversationState();
-
- // TODO: For creation one doesn't need necessarily a resource path, but a resource type definition would be sufficient, whereas for update/modify and delete one needs a resource path
- if(resourcePath == null){
- String adaptedResourceName = getResourceConfigProperty("adapted-resource-name");
- String adaptedResourceNamespace = getResourceConfigProperty("adapted-resource-namespace");
- if (adaptedResourceName != null && adaptedResourceNamespace != null) {
- log.warn("TODO: Implement initialization of resource from resource type definition: " + adaptedResourceName + ", " + adaptedResourceNamespace);
- }
- throw new IllegalStateException("The adapted resource path must be specified in the request or should be available in the conversation state");
- }
-
- Object model = null;
- if (cs == null || cs.getModel() == null) { // This is check is needed because the model is not serializable
- // Instantiate model, depending on the usecase
- if (Usecase.create.equals(usecase)) {
- CreatableV3 cv3 = getAdaptedResourceAsCreatableV3(resourcePath);
- model = cv3.getResourceInputForCreation();
- } else if (Usecase.modify.equals(usecase)) {
- ModifiableV3 mv3 = getAdaptedResourceAsModifiableV3(resourcePath);
- model = mv3.getResourceInputForModification();
- } else if (Usecase.remove.equals(usecase)) {
- DeletableV1 dv1 = getAdaptedResourceAsDeletableV1(resourcePath);
- model = dv1.getResourceInputForDeletion();
- } else {
- log.warn("Could not identify usecase");
- }
- } else {
- model = cs.getModel();
- }
-
- initConversation(model, usecase, resourcePath);
- }
-
- /**
- * Validates and calls the create() or modify() or delete() method
- */
- public void commit() throws Exception {
- java.text.DateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSZ");
- if( log.isDebugEnabled() )
- log.debug("Start commit " + dateFormat.format(new java.util.Date()));
- ConversationState cs = getConversationState();
- String path = cs.getResourcePath();
- Usecase usecase = cs.getUsecase();
-
- if (Usecase.create.equals(usecase)) {
- CreatableV3 cv3 = getAdaptedResourceAsCreatableV3(path);
- ResourceInput input = (ResourceInput) getConversationState().getModel();
- if(input.validate()){
- if( log.isDebugEnabled() )
- log.debug("Start create " + dateFormat.format(new java.util.Date()));
- cv3.create(input);
- if( log.isDebugEnabled() )
- log.debug("End create " + dateFormat.format(new java.util.Date()));
- }else{
- throw new IllegalArgumentException("The input for the adapted resource is not valid");
- }
- } else if (Usecase.modify.equals(usecase)) {
- ModifiableV3 mv3 = getAdaptedResourceAsModifiableV3(path);
- ResourceInput input = (ResourceInput) getConversationState().getModel();
- if(input.validate()){
- mv3.modify(input);
- }else{
- throw new IllegalArgumentException("The input for the adapted resource is not valid");
- }
- } else if (Usecase.remove.equals(usecase)) {
- DeletableV1 dv1 = getAdaptedResourceAsDeletableV1(path);
- ResourceInput input = (ResourceInput) getConversationState().getModel();
- if(input.validate()){
- dv1.delete(input);
- }else{
- throw new IllegalArgumentException("The input for the adapted resource is not valid");
- }
- }
- if( log.isDebugEnabled() )
- log.debug("End commit " + dateFormat.format(new java.util.Date()));
- }
-
- /**
- * If the conversation/continuation has been canceled, then react accordingly
- */
- private void doCancel() throws Exception {
- String adaptedResourcePath = getAdaptedResourcePathFromConversationState();
- log.debug("Adapted resource: " + adaptedResourcePath);
- Resource adaptedResource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), adaptedResourcePath);
- if (ResourceAttributeHelper.hasAttributeImplemented(adaptedResource, "Viewable", "2") && ResourceAttributeHelper.hasAttributeImplemented(adaptedResource, "Versionable", "2")) {
- if (((org.wyona.yanel.core.api.attributes.ViewableV2) adaptedResource).exists()) {
- ((org.wyona.yanel.core.api.attributes.VersionableV2) adaptedResource).cancelCheckout();
- } else {
- log.warn("Resource '" + adaptedResourcePath + "' does not exist!");
- }
- } else {
- log.warn("Resource '" + adaptedResourcePath + "' is not ViewableV2/VersionableV2 and hence checkout cannot be canceled!");
- }
- }
-
- /**
- * Get adapted resource path. It is either in the request or in the conversation.
- */
- private String getAdaptedResourcePathFromConversationState() {
- String adaptedResourcePath = getAdaptedResourcePath();
- ConversationState cs = getConversationState();
- if(adaptedResourcePath == null && cs != null) {
- adaptedResourcePath = cs.getResourcePath();
- }
- return adaptedResourcePath;
- }
-
- /**
- * Check if resource has the interface CreatableV3 implemented
- */
- private CreatableV3 getAdaptedResourceAsCreatableV3(String path) throws Exception {
- Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
- if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Creatable", "3")) {
- throw new Exception("The adapted resource (" + resource.getResourceTypeUniversalName() + ", " + path + ") is not of the type '" + CreatableV3.class.getName() + "'");
- }
- return (CreatableV3) resource;
- }
-
- private ModifiableV3 getAdaptedResourceAsModifiableV3(String path) throws Exception {
- Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
- if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Modifiable", "3")) {
- throw new Exception("The adapted resource is not of the type " + ModifiableV3.class.getName());
- }
- return (ModifiableV3) resource;
- }
-
- private DeletableV1 getAdaptedResourceAsDeletableV1(String path) throws Exception {
- Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
- if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Deletable", "1")) {
- throw new Exception("The adapted resource is not of the type " + DeletableV1.class.getName());
- }
- return (DeletableV1) resource;
- }
-
- protected void passParameters(Transformer transformer) throws Exception {
- super.passParameters(transformer);
- ConversationState cs = getConversationState();
- if (cs != null) {
- transformer.setParameter("resourceInput", cs.getModel());
- transformer.setParameter("url.before.conversation", cs.getRefererUrl());
- } else {
- log.warn("The conversation was not initialized");
- }
- }
-
- protected void passParameters(JellyContext jellyContext) throws Exception {
- super.passParameters(jellyContext);
- ConversationState cs = getConversationState();
- if (cs != null) {
- jellyContext.setVariable("resourceInput", cs.getModel());
- jellyContext.setVariable("url.before.conversation", cs.getRefererUrl());
- } else {
- log.warn("The conversation was not initialized");
- }
- }
-}
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,469 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import javax.xml.transform.Source;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.sax.SAXResult;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.transform.stream.StreamSource;
-
-import org.apache.avalon.framework.configuration.Configuration;
-import org.apache.avalon.framework.configuration.ConfigurationUtil;
-import org.apache.commons.jelly.JellyContext;
-import org.apache.commons.jelly.XMLOutput;
-import org.apache.xml.resolver.tools.CatalogResolver;
-import org.apache.xml.serializer.Serializer;
-import org.w3c.dom.Document;
-import org.wyona.security.core.api.Identity;
-import org.wyona.yanel.core.Constants;
-import org.wyona.yanel.core.attributes.viewable.View;
-import org.wyona.yanel.core.serialization.SerializerFactory;
-import org.wyona.yanel.core.source.SourceResolver;
-import org.wyona.yanel.core.transformation.I18nTransformer2;
-import org.wyona.yanel.core.transformation.XIncludeTransformer;
-import org.wyona.yanel.core.util.PathUtil;
-import org.wyona.yanel.impl.resources.ViewDescriptorUsingTemplate.TemplateOption;
-import org.xml.sax.InputSource;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
-
-import org.apache.log4j.Logger;
-
-/**
- * This resource type takes care only of the custom configuration.
- * It behaves much like org.wyona.yanel.impl.resources.usecase.UsecaseResource.
- * In the resource configuration one can specify views and there a Jelly template and arbitrary XSLTs.
- * <p>
- * The templates get the following parameters(subclasses may enrich this set):
- * <ul>
- * <li> <b>All the parameters from the incoming request</b>
- * <li>yanel.path.name
- * <li>yanel.path
- * <li>yanel.back2realm
- * <li>yanel.back2context
- * <li>yanel.global.htdocs
- * <li>yanel.resource.htdocs
- * <li>yanel.requested.language
- * <li>yanel.content.language
- * <li>client (i.e. user agent)
- * <li>yanel.username
- * <li>yanel.toolbar.status
- * <li>yanel.reserved.prefix
- * </ul>
- * <p>
- * The view description looks as follows:
- * <pre>
- * [yanel:custom-config]
- * [views xmlns="http://www.wyona.org/yanel/rti/1.0"]
- * [view id="..."]
- * [template type="JELLY"]...[/template]
- * [xslt]...[/xslt]*
- * [mime-type]...[/mime-type]? (e.g. text/html)
- * [serializer key="..."/]?
- * [/view]
- * [screen/]*
- * ...
- * [fragment/]*
- * [/yanel:custom-config]
- * </pre>
- * The 'fragment' view hints the view adapters that this is not the user screen but rather a fragment generated by AJAX call or something similar.
- * The 'screen' view is the synonym of 'view', used to stress that the view is not a fragment
- * */
-public abstract class JellyControllerAdapter extends ControllerAdapter {
-
- private static Logger log = Logger.getLogger(JellyControllerAdapter.class);
-
- // Jelly can be configured to escape the text that it gets from JAVA
- // This parameter allows to disable escaping.
- // Accepted values: on, off, true, false, yes, no
- public static final String ESCAPE_TEXT_IN_JELLY = "escape.text.in.jelly";
-
- private HashMap<String, ViewDescriptorUsingTemplate> viewDescriptors;
-
- public JellyControllerAdapter() {
- super();
- }
-
- /**
- * Checks if the parameter that configures the escaping was passed:
- * <ul>
- * <li> As a parameter in the request
- * <li> As a property in the configuration
- * </ul>
- * @return <code>true</code> by default, otherwise depends on the parameter value
- * */
- public boolean isEscapeTextEnabled(){
- String escape = getParameterAsString(ESCAPE_TEXT_IN_JELLY);
- if(escape == null){
- try {
- escape = getConfiguration().getProperty(ESCAPE_TEXT_IN_JELLY);
- } catch (Exception e) {
- log.info("Could not detect the property "+ESCAPE_TEXT_IN_JELLY+", will use default value");
- }
- }
-
- if(escape == null){
- return true;
- }
- if("on".equals(escape.trim().toLowerCase()) || "yes".equals(escape.trim().toLowerCase()) || Boolean.valueOf(escape.trim())){
- return true;
- }
- return false;
- }
-
- /**
- *
- */
- public ViewDescriptorUsingTemplate[] getViewDescriptors() {
- if (this.viewDescriptors == null) {
- try {
- this.viewDescriptors = new HashMap<String, ViewDescriptorUsingTemplate>();
-
- // reads views from configuration:
- if (getConfiguration() != null && getConfiguration().getCustomConfiguration() != null) {
- Document customConfigDoc = getConfiguration().getCustomConfiguration();
- Configuration config = ConfigurationUtil.toConfiguration(customConfigDoc.getDocumentElement());
- Configuration viewsConfig = config.getChild("views");
-
- {
- ViewDescriptorUsingTemplate defaultView = null;
-
- // Collect view definitions
- Configuration[] viewConfigs = viewsConfig.getChildren("view");
- for (int i = 0; i < viewConfigs.length; i++) {
- String id = normalize(viewConfigs[i].getAttribute("id"));
-
- ViewDescriptorUsingTemplate viewDescriptor = new ViewDescriptorUsingTemplate(id, viewConfigs[i]);
-
- if(Constants.Request.DEFAULT_VIEW_ID.equals(id)){
- defaultView = viewDescriptor;
- }
-
- this.viewDescriptors.put(id, viewDescriptor);
- }
-
- if(defaultView == null){
- defaultView = new ViewDescriptorUsingTemplate(Constants.Request.DEFAULT_VIEW_ID);
- defaultView.setMimeType("application/xml");
- this.viewDescriptors.put(Constants.Request.DEFAULT_VIEW_ID, defaultView);
- }
- }
-
- {
- // Collect fragment definitions
- //log.debug("Get all fragments ...");
- Configuration[] fragmentConfigs = viewsConfig.getChildren("fragment");
- for (int i = 0; i < fragmentConfigs.length; i++) {
- String id = normalize(fragmentConfigs[i].getAttribute("id"));
- if(log.isDebugEnabled()) log.debug("Fragment ID: " + id);
-
- ViewDescriptorUsingTemplate viewDescriptor = new ViewDescriptorUsingTemplate(id, fragmentConfigs[i], true);
- if(this.viewDescriptors.keySet().contains(id)){
- log.warn("The view with ID '" + id + "' will be ignored as the descriptor with such an ID already exists!");
- }else{
- this.viewDescriptors.put(id, viewDescriptor);
- }
- }
- }
- }
- } catch (Exception e) {
- throw new IllegalArgumentException("Views are not properly configured");
- }
- }
-
- return this.viewDescriptors.values().toArray(new ViewDescriptorUsingTemplate[this.viewDescriptors.size()]);
- }
-
- /**
- * Runs the pipeline of templates. You can feed an XML, JELLY or XSLT to the pipe.
- * Every template will be processed and passed through internationalization and XInclude transformers.
- * So every subsequent template can react to the expanded(resolved) input.
- */
- public View getView(String viewId) throws Exception {
- View view = new View();
-
- viewId = normalize(viewId);
-
- ViewDescriptorUsingTemplate viewDescriptor = (ViewDescriptorUsingTemplate)getViewDescriptor(viewId);
- view.setMimeType(viewDescriptor.getMimeType());
-
- try {
- Serializer serializer = viewDescriptor.getSerializer();
- Serializer xmlSerializer = SerializerFactory.getSerializer(SerializerFactory.XML);
-
- InputStream content = null;
-
- TemplateOption [] templates = viewDescriptor.getTemplates();
- //TODO: don't serialize on each template, rather pipe content handlers
- for (int i = 0; i < templates.length; i++) {
- // The first one is JELLY or XML, then every other is XSLT
- if(i + 1 == templates.length){
- // Use configured serializer for the last template only
- content = processTemplate(content, templates[i], serializer);
- }else{
- // In the intermediate steps only XML is produced
- content = processTemplate(content, templates[i], xmlSerializer);
- }
- }
-
- // write result into view:
- view.setInputStream(content);
- } catch(Exception e) {
- log.error(e + " (" + getPath() + ", " + getRealm() + ")", e);
- throw new Exception(e);
- }
-
- return view;
- }
-
- /**
- * @param content Template may be applied to it, e.g. for transformations
- * @param template Template
- * @param serializer Serializer
- *
- * @return result of the processing as a stream
- */
- protected InputStream processTemplate(InputStream content, TemplateOption template, Serializer serializer) throws Exception{
- InputStream result = null;
-
- SourceResolver uriResolver = new SourceResolver(this);
- CatalogResolver catalogResolver = new CatalogResolver();
-
- InputStream templateInputStream = getTemplateAsStream(template);
-
- if(TemplateOption.TYPE_XSLT.equals(template.getType())){
- // Here after the required template it is
-
- // create xslt transformer:
- SAXTransformerFactory tf = (SAXTransformerFactory)TransformerFactory.newInstance();
- tf.setURIResolver(uriResolver);
-
- // attach handler:
- TransformerHandler th = tf.newTransformerHandler(new StreamSource(templateInputStream));
-
- th.getTransformer().setURIResolver(uriResolver);
- Transformer transformer = th.getTransformer();
-
- passParameters(transformer);
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- serializer.setOutputStream(baos);
- th.setResult(new SAXResult(serializer.asContentHandler()));
-
- // create reader:
- XMLReader xmlReader = XMLReaderFactory.createXMLReader();
- xmlReader.setEntityResolver(catalogResolver);
- xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
- xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", th);
- xmlReader.setContentHandler(th);
-
- // execute:
- xmlReader.parse(new InputSource(content));
-
- result = new ByteArrayInputStream(baos.toByteArray());
- }else if(TemplateOption.TYPE_JELLY.equals(template.getType())){
- log.debug("Template type: " + template.getType());
- JellyContext jellyContext = new JellyContext();
- passParameters(jellyContext);
-
- ByteArrayOutputStream jellyResultStream = new ByteArrayOutputStream();
-
- XMLOutput jellyOutput = XMLOutput.createXMLOutput(jellyResultStream, isEscapeTextEnabled());
- jellyContext.runScript(new InputSource(templateInputStream), jellyOutput);
- jellyOutput.flush();
- result = new ByteArrayInputStream(jellyResultStream.toByteArray());
- }else{
- result = templateInputStream;
- }
-
- //
- // Pass the result through i18n and XInclude transformers
- //
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- serializer.setOutputStream(baos);
-
- // create i18n transformer:
- I18nTransformer2 i18nTransformer = new I18nTransformer2(getI18NCatalogueNames(), getRequestedLanguage(), getRealm().getDefaultLanguage());
- i18nTransformer.setEntityResolver(catalogResolver);
-
-
- // create xinclude transformer:
- XIncludeTransformer xIncludeTransformer = new XIncludeTransformer();
- xIncludeTransformer.setResolver(uriResolver);
-
- // chain everything together (create a pipeline):
- xIncludeTransformer.setResult(new SAXResult(i18nTransformer));
- i18nTransformer.setResult(new SAXResult(serializer.asContentHandler()));
-
- // create reader and execute
- XMLReader xmlReader = XMLReaderFactory.createXMLReader();
- xmlReader.setEntityResolver(catalogResolver);
- xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
-
- xmlReader.setContentHandler(new SAXResult(xIncludeTransformer).getHandler());
- xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", xIncludeTransformer);
-
- xmlReader.setContentHandler(new SAXResult(i18nTransformer).getHandler());
- xmlReader.parse(new InputSource(result));
-
- result = new ByteArrayInputStream(baos.toByteArray());
-
- return result;
- }
-
- /**
- * Tries to get the template as input stream. The template can be either accessible through
- * a URL or simply from the repository.
- *
- * @param template TemplateOption containing type and URL
- */
- protected InputStream getTemplateAsStream(TemplateOption template)throws Exception{
- log.debug("Template: " + template.getUrl());
- InputStream templateInputStream = null;
- if(!template.getUrl().startsWith("/")){
- // Resolve the URI into the InputStream
- Source src = new SourceResolver(this).resolve(template.getUrl(), null);
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- TransformerFactory.newInstance().newTransformer().transform(src, new StreamResult(bos));
- templateInputStream = new ByteArrayInputStream(bos.toByteArray());
- }else{
- // Just take the template from the repository node
- templateInputStream = getRealm().getRepository().getNode(template.getUrl()).getInputStream();
- }
- return templateInputStream;
- }
-
- /**
- * Pass parameters to Jelly context (Add more parameters in subclasses if needed)
- */
- protected void passParameters(JellyContext jellyContext) throws Exception{
- // Attach all parameters that came with the request, such that templates can make use of them.
- // NOTE: all parameter values will be of type String. In XSLT: <param name="p" value="'actual_value'"/>
- for (Iterator i = getParameters().entrySet().iterator(); i.hasNext();) {
- Map.Entry entry = (Map.Entry) i.next();
- jellyContext.setVariable(String.valueOf(entry.getKey()), entry.getValue());
- }
-
- // Set general parameters
- jellyContext.setVariable("resource", this); // TODO: Somehow this doesn't work!
-
- jellyContext.setVariable("continuation.id", getContinuation().getId());
- jellyContext.setVariable("continuation.id.key", YANEL_CONTINUATION_ID);
-
- jellyContext.setVariable("yanel.path.name", org.wyona.commons.io.PathUtil.getName(getPath()));
- jellyContext.setVariable("yanel.path", getPath());
-
- jellyContext.setVariable("yanel.back2realm", PathUtil.backToRealm(getPath()));
- jellyContext.setVariable("yanel.back2context", PathUtil.backToContext(realm, getPath()));
-
- jellyContext.setVariable("yanel.global.htdocs", PathUtil.getGlobalHtdocsPath(this));
- jellyContext.setVariable("yanel.resource.htdocs", PathUtil.getResourcesHtdocsPath(this));
-
- jellyContext.setVariable("yanel.requested.language", getRequestedLanguage());
- jellyContext.setVariable("yanel.content.language", getContentLanguage());
-
- String userAgent = getEnvironment().getRequest().getHeader("User-Agent");
- String client = getClient(userAgent);
- if (client != null) jellyContext.setVariable("client", client);
-
- // username
- String username = getUsername();
- if (username != null) jellyContext.setVariable("yanel.username", username);
-
- // Add toolbar status
- String toolbarStatus = getToolbarStatus();
- if (toolbarStatus != null) jellyContext.setVariable("yanel.toolbar.status", toolbarStatus);
-
- jellyContext.setVariable("yanel.reserved.prefix", getYanel().getReservedPrefix());
- }
-
- /**
- * Add more parameters in subclasses if needed
- * */
- protected void passParameters(Transformer transformer) throws Exception{
- // Attach all parameters that came with the request. Templates can make use of them.
- // NOTE: all parameter values will be of type String. In XSLT: <param name="p" value="'actual_value'"/>
- for (Iterator i = getParameters().entrySet().iterator(); i.hasNext();) {
- Map.Entry entry = (Map.Entry) i.next();
- if (entry.getValue() instanceof String) {
- String value = (String) entry.getValue();
- transformer.setParameter(String.valueOf(entry.getKey()), value);
- } else if(entry.getValue() instanceof String[]){
- // values separated by a space
- String separator = " ";
-
- StringBuffer finalValue = new StringBuffer();
- String [] values = (String[]) entry.getValue();
- for (int j = 0; j < values.length; j++) {
- finalValue.append(values[j]);
- if(j + 1 != values.length){
- finalValue.append(separator);
- }
- }
- transformer.setParameter(String.valueOf(entry.getKey()), finalValue);
- } else{
- // Never happens
- }
- }
-
- // Set general parameters
- transformer.setParameter("yanel.path.name", org.wyona.commons.io.PathUtil.getName(getPath()));
- transformer.setParameter("yanel.path", getPath());
-
- transformer.setParameter("yanel.back2realm", PathUtil.backToRealm(getPath()));
- transformer.setParameter("yanel.back2context", PathUtil.backToContext(realm, getPath()));
-
- transformer.setParameter("yanel.global.htdocs", PathUtil.getGlobalHtdocsPath(this));
- transformer.setParameter("yanel.resource.htdocs", PathUtil.getResourcesHtdocsPath(this));
-
- transformer.setParameter("yanel.requested.language", getRequestedLanguage());
- transformer.setParameter("yanel.content.language", getContentLanguage());
-
- String userAgent = getEnvironment().getRequest().getHeader("User-Agent");
- String client = getClient(userAgent);
- if (client != null) transformer.setParameter("client", client);
-
- // username
- String username = getUsername();
- if (username != null) transformer.setParameter("yanel.username", username);
-
- // Add toolbar status
- String toolbarStatus = getToolbarStatus();
- if (toolbarStatus != null) transformer.setParameter("yanel.toolbar.status", toolbarStatus);
-
- transformer.setParameter("yanel.reserved.prefix", getYanel().getReservedPrefix());
- }
-
- protected final String getUsername() {
- Identity identity = getEnvironment().getIdentity();
- if (identity != null) return identity.getUsername();
- return null;
- }
-
- protected final String getClient(String userAgent) {
- if (userAgent.indexOf("Firefox") > 0) {
- return "firefox";
- } else if (userAgent.indexOf("MSIE") > 0) {
- return "msie";
- } else {
- log.warn("Client could not be recognized: " + userAgent);
- return null;
- }
- }
-
- protected final String getToolbarStatus() {
- // TODO: Use YanelServlet.TOOLBAR_KEY instead "toolbar"!
- return (String) getEnvironment().getRequest().getSession(true).getAttribute("toolbar");
- }
-
-}
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,139 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import javax.xml.transform.Transformer;
-
-import org.apache.commons.jelly.JellyContext;
-
-import org.apache.log4j.Logger;
-
-/**
- * Deals with conversations.
- * A conversation is mapped to a path.
- * */
-//TODO: is path ok as a key of the conversation state
-//TODO[high]: conversation state object must be synchronized, because it is put into the
-//session and may be accessed by different browser instances
-abstract class JellyConversationAdapter extends JellyControllerAdapter {
-
- private static Logger log = Logger.getLogger(JellyConversationAdapter.class);
-
- /**
- * This is the view where the user should get after all the activities are finished.
- * */
- protected static final String DONE_VIEW_ID = "done";
-
- /**
- * This is the view where the user should get when the conversation is canceled.
- * */
- protected static final String CANCEL_VIEW_ID = "cancel";
-
- /**
- * This parameter is useful when the referrer url is not enough.
- * For example, the page may needs to go to some page (other than a referrer) after the conversation has been completed.
- * */
- public static final String PARAM_GOTO_URL = "goto.url";
-
- private ConversationState conversationState;
-
- public JellyConversationAdapter() {
- super();
- supportedViewIds.add(CANCEL_VIEW_ID);
- supportedViewIds.add(DONE_VIEW_ID);
- }
-
- /**
- * Initialize or keep up to date the conversation state
- */
- protected abstract void init() throws Exception;
-
- /**
- * In this method the conversation should be commited. Data may need to be validated befor commit.
- */
- public abstract void commit()throws Exception;
-
-
- /**
- * Returns the conversation state object. It may be available in the session
- * or invalidated (no more in the session).
- * @return - <code>null</code> when called before initConversation(), in other cases the conversation state object (maybe invalidated already)
- * */
- protected final ConversationState getConversationState(){
- if(conversationState == null){
- conversationState = (ConversationState)getEnvironment().getRequest().getSession(true).getAttribute(getPath());
- }
- return conversationState;
- }
-
- /**
- * Checks if the conversation is already in the session and creates it if necessary
- * */
- protected final ConversationState initConversation(Object model, Usecase usecase, String resourcePath){
- log.warn("Continuation: " + getContinuation().getId());
-
- ConversationState cs = new ConversationState(model);
- cs.setUsecase(usecase);
- cs.setResourcePath(resourcePath);
-
- ConversationState current = getConversationState();
- if (current != null && !current.isInvalidated()) {
- // Refresh the properties, because they are changing during the conversation
- current.setModel(cs.getModel());
- current.setCurrentScreen(cs.getCurrentScreen());
- } else {
- cs.setRefererUrl(getEnvironment().getRequest().getHeader("Referer"));
- // The new conversation state becomes current
- current = cs;
- }
-
- String gotoUrl = getParameterAsString(PARAM_GOTO_URL);
- if(gotoUrl != null){
- current.setGotoUrl(gotoUrl);
- }
-
- log.warn("Attach conversation state for usecase '" + current.getUsecase() + "' to session with id '" + getPath() + "'");
- getEnvironment().getRequest().getSession(true).setAttribute(getPath(), current);
- return current;
- }
-
- /**
- * Removes the conversation state from the session and invalidates the conversation state object.
- * */
- protected final void destroyConversation(){
- ConversationState current = getConversationState();
- if(current != null){
- current.invalidate();
- }
- getEnvironment().getRequest().getSession(true).removeAttribute(getPath());
- }
-
- /**
- * No additional parameters passed. Subclasses should consult conversation state for additional parameters
- */
- protected void passParameters(JellyContext jellyContext) throws Exception {
- super.passParameters(jellyContext);
- ConversationState cs = getConversationState();
- if (cs != null) {
- if(cs.getGotoUrl() != null){
- jellyContext.setVariable(PARAM_GOTO_URL, cs.getGotoUrl());
- }
- } else {
- log.warn("The conversation was not initialized");
- }
-
- }
-
- /**
- * No additional parameters passed. Subclasses should consult conversation state for additional parameters
- */
- protected void passParameters(Transformer transformer) throws Exception {
- super.passParameters(transformer);
- ConversationState cs = getConversationState();
- if (cs != null) {
- if(cs.getGotoUrl() != null){
- transformer.setParameter(PARAM_GOTO_URL, cs.getGotoUrl());
- }
- } else {
- log.warn("The conversation was not initialized");
- }
- }
-}
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,51 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-
-/**
- * The adapter knows how to adapt resources. It works as a facade
- * for adapted resource that are implementing some specific functionality.
- * E.g. a resource can be creatable, deletable, modifiable...
- * <p>
- * Normally the adapter should be instantiated by Yanel servlet
- * */
-public interface ResourceAdapter{
- public static final String PARAM_ADAPTED_RESOURCE_PATH = "adapted.resource.path";
-
- public static enum Usecase{
- /**
- * If this usecase is specified, then a resource of the specified type will create something.
- */
- create,
- /**
- * If this usecase is specified, then a resource of the specified type will modify something.
- */
- modify,
- /**
- * If this usecase is specified, then a resource of the specified type will delete something.
- */
- //TODO: the value must be "delete", but YanelServlet does not allow that?
- remove;
-
- /**
- * Create the usecase out of the given parameter ignoring the case of the string.
- * Behaves much like simple valueOf
- * @return null when the parameter is null, otherwise tries to create the usecase.
- * */
- public static Usecase caseInsensitiveValueOf(String usecase){
- if(usecase == null){
- return null;
- }
-
- return Usecase.valueOf(usecase.toLowerCase());
- }
- }
-
- public String getAdaptedResourcePath();
- public void setAdaptedResourcePath(String adaptedResourcePath);
-
- /**
- * The adapter knows how to adapt the resource for specific usecases
- * */
- public Usecase getUsecase();
- public void setUsecase(Usecase usecase);
-}
Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java 2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,195 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.util.Enumeration;
-import java.util.Properties;
-
-import org.apache.avalon.framework.configuration.Configuration;
-import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.xml.serializer.Serializer;
-import org.wyona.commons.io.MimeTypeUtil;
-import org.wyona.yanel.core.attributes.viewable.ViewDescriptor;
-import org.wyona.yanel.core.serialization.SerializerFactory;
-
-/**
- * A view is configured with a template and a set of XSLTs:
- * <pre>
- * [view id="x"]
- * [template type="JELLY"]...location...[/template]
- * [xslt]_location_[/xslt]
- * ...
- * [xslt]_location_[/xslt]
- * [mime-type]...(test/html)...[mime-type]
- * [serializer key="...(XHTML_STRICT)..."]
- * ... serializer props...
- * [/serializer]
- *
- * [/view]
- * </pred>
- * */
-public class ViewDescriptorUsingTemplate extends ViewDescriptor {
-
- /**
- *
- */
- public static class TemplateOption{
- public static final String TYPE_JELLY = "JELLY";
- public static final String TYPE_XSLT = "XSLT";
- /**
- * This is to say that it is raw XML
- */
- public static final String TYPE_XML = "XML";
-
- private String type;
- private String url;
-
- public TemplateOption(String type, String url) {
- this.type = type;
- this.url = url;
- }
-
- public String getType() {
- return type;
- }
-
- public String getUrl() {
- return url;
- }
- }
-
- private boolean fragment = false;
-
- protected TemplateOption [] templates = new TemplateOption[0];
-
- protected String serializerKey = SerializerFactory.XHTML_STRICT_KEY;
- protected Properties serializerProperties = new Properties();
-
- protected ViewDescriptorUsingTemplate(String id){
- super(id);
- }
-
- public ViewDescriptorUsingTemplate(String id, Configuration viewDefinition) throws ConfigurationException{
- this(id, viewDefinition, false);
- }
-
- public ViewDescriptorUsingTemplate(String id, Configuration viewDefinition, boolean isFragment) throws ConfigurationException{
- super(id);
- configure(viewDefinition);
- this.fragment = isFragment;
- }
-
- /**
- * Defaults:
- * <p>
- * mime-type : text/html<br>
- * serializer: XHTML_STRICT<br>
- * serializer props: empty
- */
- protected void configure(Configuration config) throws ConfigurationException {
- Configuration[] templateConfigs = config.getChildren("template");
- if(templateConfigs.length == 0){
- throw new ConfigurationException("A template is not specified");
- }
-
- Configuration[] xsltConfigs = config.getChildren("xslt");
-
- templates = new TemplateOption[1 + xsltConfigs.length];
-
- templates[0] = new TemplateOption(templateConfigs[0].getAttribute("type"), templateConfigs[0].getValue());
- for (int i = 0; i < xsltConfigs.length; i++) {
- templates[i+1] = new TemplateOption(TemplateOption.TYPE_XSLT, xsltConfigs[i].getValue());
- }
-
- Configuration mimeTypeConfig = config.getChild("mime-type", false);
- if (mimeTypeConfig != null) {
- setMimeType(mimeTypeConfig.getValue());
- }else{
- setMimeType("text/html");
- }
-
- Configuration serializerConfig = config.getChild("serializer", false);
- if (serializerConfig != null) {
- serializerKey = (serializerConfig.getAttribute("key") == null ? serializerKey : serializerConfig.getAttribute("key"));
- serializerProperties = new Properties();
- Configuration propertyConfig = serializerConfig.getChild("omit-xml-declaration", false);
- if (propertyConfig != null) {
- serializerProperties.setProperty("omit-xml-declaration", propertyConfig.getValue());
- }
- propertyConfig = serializerConfig.getChild("doctype-public", false);
- if (propertyConfig != null) {
- serializerProperties.setProperty("doctype-public", propertyConfig.getValue());
- }
- propertyConfig = serializerConfig.getChild("doctype-system", false);
- if (propertyConfig != null) {
- serializerProperties.setProperty("doctype-sytem", propertyConfig.getValue());
- }
- }
- }
-
- public String getSerializerKey() {
- return serializerKey;
- }
-
- public void setSerializerKey(String serializerKey) {
- this.serializerKey = serializerKey;
- }
-
- public Properties getSerializerProperties() {
- return serializerProperties;
- }
-
- public void setSerializerProperties(Properties serializerProperties) {
- this.serializerProperties = serializerProperties;
- }
-
- public TemplateOption[] getTemplates() {
- return templates;
- }
-
- /**
- * @return <code>true</code> when the view represents a fragment, not a screen.
- * For instance, a fragment view is useful when doing AJAX call
- * */
- public boolean isFragment(){
- return fragment;
- }
-
- /**
- * Creates an html or xml serializer for this view descriptor
- */
- public Serializer getSerializer() throws Exception {
- Serializer serializer = null;
- String serializerKey = getSerializerKey();
- if (serializerKey != null) {
- serializer = SerializerFactory.getSerializer(serializerKey);
- if (serializer == null) {
- throw new Exception("could not create serializer for key: " + serializerKey);
- }
- } else {
- String mimeType = getMimeType();
-
- if (MimeTypeUtil.isHTML(mimeType) && !MimeTypeUtil.isXML(mimeType)) {
- serializer = SerializerFactory.getSerializer(SerializerFactory.HTML_TRANSITIONAL);
- } else if (MimeTypeUtil.isHTML(mimeType) && MimeTypeUtil.isXML(mimeType)){
- serializer = SerializerFactory.getSerializer(SerializerFactory.XHTML_STRICT);
- } else if (MimeTypeUtil.isXML(mimeType)) {
- serializer = SerializerFactory.getSerializer(SerializerFactory.XML);
- } else if (MimeTypeUtil.isTextual(mimeType)) {
- serializer = SerializerFactory.getSerializer(SerializerFactory.TEXT);
- } else{
- serializer = SerializerFactory.getSerializer(SerializerFactory.XHTML_STRICT);
- }
- }
- // allow to override xml declaration and doctype:
- Properties properties = getSerializerProperties();
- if (properties != null) {
- Enumeration propNames = properties.propertyNames();
- while (propNames.hasMoreElements()) {
- String name = (String)propNames.nextElement();
- String value = properties.getProperty(name);
-
- serializer.getOutputFormat().setProperty(name, value);
- }
- }
- return serializer;
- }
-}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/BasicFormResource.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/BasicFormResource.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/BasicFormResource.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,324 @@
+package org.wyona.yanel.impl.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map.Entry;
+
+import org.apache.log4j.Logger;
+import org.wyona.yanel.core.ResourceNotFoundException;
+import org.wyona.yanel.core.attributes.viewable.View;
+
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+/**
+ * Base class to handle forms. Returns the following XML streams:
+ *
+ * <p>Initial call:</p>
+ * <pre>
+ * <form-resource>
+ * <form/>
+ * </form-resource>
+ * </pre>
+ *
+ * <p>Validation failed:</p>
+ * <pre>
+ * <form-resource>
+ * <form/>
+ * <validation/>
+ * <input/>
+ * <result/>
+ * </form-resource>
+ * </pre>
+ *
+ * <p>Successful processing:</p>
+ * <pre>
+ * <form-resource>
+ * <confirmation/>
+ * <input/>
+ * <result/>
+ * </form-resource>
+ * </pre>
+ *
+ * <p>Error occurred:</p>
+ * <pre>
+ * <form-resource>
+ * <error/>
+ * <input/>
+ * <result/>
+ * </form-resource>
+ * </pre>
+ *
+ * @author Matthias Leumann
+ *
+ */
+public abstract class BasicFormResource extends BasicXMLResource {
+
+ private static final Logger log = Logger.getLogger(BasicFormResource.class);
+
+ private static final String ENCODING = "UTF-8";
+ private static final String INPUT_SUBMIT = "submit";
+
+ protected static final String RESULT_CODE_DONE_SUCCESSFUL = "done-successful";
+ private static final String RESULT_CODE_FAILED_INVALID = "failed-invalid";
+ private static final String RESULT_CODE_FAILED_ERROR = "failed-error";
+
+ private String resultCode;
+ private StringBuilder resultXml = new StringBuilder();
+ private LinkedList<NameValuePair> validationErrors = new LinkedList<NameValuePair>();
+
+ public View getView(String viewId) throws Exception {
+ String sslRedirectParam = getResourceConfigProperty("ssl-redirect");
+ if (sslRedirectParam != null && sslRedirectParam.equals("true")) {
+ if (!getEnvironment().getRequest().isSecure() && getEnvironment().getRequest().getParameter("ssl") == null) {
+ View view = new View();
+ view.setResponse(false);
+ URL sslURL;
+ int sslPort = 443;
+ if (getRealm().isProxySet()) {
+ if (realm.getProxySSLPort() >= 0) sslPort = realm.getProxySSLPort();
+ String requestURI = getEnvironment().getRequest().getRequestURI();
+ if (realm.getProxyPrefix() != null) requestURI = requestURI.substring(realm.getProxyPrefix().length());
+ sslURL = new URL("https", getRealm().getProxyHostName(), sslPort, requestURI + "?ssl=true");
+ } else {
+ sslURL = new URL("https", getEnvironment().getRequest().getServerName(), sslPort, getEnvironment().getRequest().getRequestURI() + "?ssl=true");
+ }
+ log.warn("Redirect to SSL: " + sslURL.toString());
+ getEnvironment().getResponse().setHeader("Location", sslURL.toString());
+ getEnvironment().getResponse().setStatus(javax.servlet.http.HttpServletResponse.SC_TEMPORARY_REDIRECT);
+ return view;
+ }
+ }
+ return super.getView(viewId);
+ }
+
+ protected final InputStream getContentXML(String viewId) throws Exception {
+ if (!exists()) {
+ log.warn("No such resource: " + getPath());
+ throw new ResourceNotFoundException("No such resource: " + getPath());
+ }
+
+ resultXml.append("<form-resource xmlns=\""+getNamespaceURI()+"\">");
+ if (isFormSubmitted()) {
+ validate();
+ if (isInputValid()) {
+ try {
+ resultCode = execute();
+ appendConfirmation();
+ } catch (Exception e) {
+ log.error(e, e);
+ resultCode = RESULT_CODE_FAILED_ERROR;
+ appendError(e);
+ }
+ } else {
+ resultCode = RESULT_CODE_FAILED_INVALID;
+ appendForm();
+ appendValidation();
+ }
+ appendInput();
+ appendResult();
+ } else {
+ appendForm();
+ }
+ resultXml.append("</form-resource>");
+ return new ByteArrayInputStream(resultXml.toString().getBytes(ENCODING));
+ }
+
+ /**
+ * Adds a validation error. Use this method to report invalid input.
+ *
+ * @param inputName name of the input parameter
+ * @param validationMessage validation message (may use i18n key)
+ */
+ protected final void addValidationError(String inputName, String validationMessage) {
+ validationErrors.add(new NameValuePair(inputName, validationMessage));
+ }
+
+ /**
+ * @deprecated Use org.wyona.yanel.core.util.MailUtil.send() instead
+ *
+ * Sends an email. E.g. use this method to send confirmation mails.
+ *
+ * @param from sender email address
+ * @param replyTo email address (if null, then no reply-to will be set)
+ * @param to receiver email address
+ * @param subject email subject
+ * @param content email content
+ */
+ protected void sendMail(String from, String replyTo, String to, String subject, String content) throws Exception {
+ log.warn("DEPRECATED");
+ org.wyona.yanel.core.util.MailUtil.send(from, replyTo, to, subject, content);
+
+
+/*
+ // Create a mail session
+ java.util.Properties props = new java.util.Properties();
+ props.put("mail.smtp.host", getResourceConfigProperty("smtpHost"));
+ props.put("mail.smtp.port", "" + getResourceConfigProperty("smtpPort"));
+ // TODO: http://java.sun.com/products/javamail/javadocs/javax/mail/Session.html
+ Session session = Session.getDefaultInstance(props, null);
+
+ // Construct the message
+ Message msg = new MimeMessage(session);
+ msg.setFrom(new InternetAddress(from));
+ if (replyTo != null) {
+ InternetAddress[] replyToAddresses = new InternetAddress[1];
+ replyToAddresses[0] = new InternetAddress(replyTo);
+ msg.setReplyTo(replyToAddresses);
+ }
+ if( log.isDebugEnabled() )
+ log.debug("From: " + msg.getFrom() + ", " + from);
+ msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
+ msg.setSubject(subject);
+ msg.setText(content);
+*/
+
+/*
+ msg.setSubject(subject,"utf-8");
+ msg.setContent(content, "text/plain;charset=utf-8");
+*/
+
+/*
+ // Send the message
+ Transport.send(msg);
+*/
+ }
+
+ /**
+ * Generates valid XML containing the data to display the form screen.
+ *
+ * @return valid XML string
+ */
+ protected abstract String generateForm() throws Exception;
+
+ /**
+ * Generates valid XML containing the data to display the confirmation screen.
+ *
+ * @return valid XML string
+ */
+ protected abstract String generateConfirmation() throws Exception;
+
+ /**
+ * Validates the form input. Use the method <code>addValidationError</code> to report invalid input.
+ */
+ protected abstract void validate() throws Exception;
+
+ /**
+ * Executes custom operation if the form input was successfully validated.
+ *
+ * @return result code (e.g. "done-successful")
+ * @throws Exception if processing failed.
+ */
+ protected abstract String execute() throws Exception;
+
+ /**
+ * @return the name space URI of of the resulting XML.
+ */
+ protected abstract String getNamespaceURI();
+
+
+ /**
+ * @return returns true if and only if the input parameters contain a parameter "submit".
+ */
+ private boolean isFormSubmitted() {
+ return getParameterAsString(INPUT_SUBMIT) != null;
+ }
+
+ /**
+ * @return true if and only if there are no validation errors.
+ */
+ private boolean isInputValid() {
+ return validationErrors.isEmpty();
+ }
+
+ /**
+ * Appends the result code to the result XML.
+ */
+ private void appendResult() {
+ resultXml.append("<result>");
+ resultXml.append(resultCode);
+ resultXml.append("</result>");
+ }
+
+ /**
+ * Appends the data for the form screen to the result XML.
+ */
+ private void appendForm() throws Exception {
+ resultXml.append("<form>");
+ String form = generateForm();
+ if (form != null) {
+ resultXml.append(form);
+ }
+ resultXml.append("</form>");
+ }
+
+ /**
+ * Appends the data for the confirmation screen to the result XML.
+ */
+ private void appendConfirmation() throws Exception {
+ resultXml.append("<confirmation>");
+ String confirmation = generateConfirmation();
+ if (confirmation != null) {
+ resultXml.append(confirmation);
+ }
+ resultXml.append("</confirmation>");
+ }
+
+ /**
+ * Appends the validation messages to the result XML.
+ */
+ private void appendValidation() {
+ resultXml.append("<validation>");
+ for (NameValuePair error : validationErrors) {
+ resultXml.append("<"+error.getName()+">"+error.getValue()+"</"+error.getName()+">");
+ }
+ resultXml.append("</validation>");
+ }
+
+ /**
+ * Appends the form input to the result XML.
+ */
+ private void appendInput() {
+ resultXml.append("<input>");
+ Iterator it = getParameters().entrySet().iterator();
+ while (it.hasNext()) {
+ Entry param = (Entry)it.next();
+ resultXml.append("<"+param.getKey()+"><![CDATA["+param.getValue()+"]]></"+param.getKey()+">");
+ }
+ resultXml.append("</input>");
+ }
+
+ private void appendError(Exception e) {
+ resultXml.append("<error>");
+ resultXml.append("<![CDATA[" + e.toString() + "]]>");
+ resultXml.append("</error>");
+ }
+
+ /**
+ * Private utility class to add validation errors to the list.
+ */
+ private class NameValuePair {
+ private String name;
+ private String value;
+
+ public NameValuePair(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/Continuation.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/Continuation.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/Continuation.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,31 @@
+package org.wyona.yanel.impl.resources;
+
+/**
+ *
+ */
+public class Continuation {
+
+ private String id;
+
+ /**
+ *
+ */
+ public Continuation() {
+ // TODO: Use UUID
+ id = "" + new java.util.Date().getTime();
+ }
+
+ /**
+ *
+ */
+ public Continuation(String id) {
+ this.id = id;
+ }
+
+ /**
+ *
+ */
+ public String getId() {
+ return id;
+ }
+}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ControllerAdapter.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ControllerAdapter.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ControllerAdapter.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,156 @@
+package org.wyona.yanel.impl.resources;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.wyona.yanel.core.Constants;
+import org.wyona.yanel.core.Resource;
+import org.wyona.yanel.core.api.attributes.ViewableV2;
+import org.wyona.yanel.core.attributes.viewable.ViewDescriptor;
+
+abstract class ControllerAdapter extends Resource implements ResourceAdapter, ViewableV2 {
+ private static Logger log = Logger.getLogger(ControllerAdapter.class);
+ public static String YANEL_CONTINUATION_ID = "yanel.continuation.id";
+
+ /**
+ * Resource config property for internationalization
+ * */
+ //TODO: what is that???
+ private static final String I18N_CATALOG_PROPERTY = "i18n-catalogue";
+
+ protected final Set<String> supportedViewIds = new HashSet<String>();
+
+ private String adaptedResourcePath = null;
+
+ private Usecase usecase = null;
+
+ /**
+ * Constants.Request.DEFAULT_VIEW_ID is registered
+ */
+ public ControllerAdapter() {
+ supportedViewIds.add(Constants.Request.DEFAULT_VIEW_ID);
+ }
+
+ /**
+ * Checks if the path was set, otherwise
+ * looks into available parameters and returns the value of
+ * PARAM_ADAPTED_RESOURCE_PATH
+ * @return null when the parameter is not found
+ * */
+ public final String getAdaptedResourcePath() {
+ if(adaptedResourcePath == null){
+ adaptedResourcePath = getParameterAsString(PARAM_ADAPTED_RESOURCE_PATH);
+ }
+ return adaptedResourcePath;
+ }
+
+ public void setAdaptedResourcePath(String adaptedResourcePath) {
+ this.adaptedResourcePath = adaptedResourcePath;
+ }
+
+ /**
+ * Checks if the usecase was set, otherwise looks into available parameters
+ * @return null when usecase can't be determined
+ * */
+ public final Usecase getUsecase() {
+ if(usecase == null){
+ String u = getParameterAsString(Constants.Request.YANEL_RESOURCE_USECASE);
+ if (u == null) {
+ try {
+ u = getResourceConfigProperty("usecase");
+ } catch (Exception e) {
+ log.error(e, e);
+ }
+ if (u == null) {
+ log.error("No usecase (neither 'create' nor 'update/modify' nor 'remove/delete') has been specified (neither within conversation nor query string nor resource config)!");
+ return null;
+ }
+ }
+ usecase = Usecase.caseInsensitiveValueOf(u);
+ }
+ return usecase;
+ }
+
+ public void setUsecase(Usecase usecase) {
+ this.usecase = usecase;
+ }
+
+ /**
+ * Get view descriptor
+ * @param viewId For example 'video'
+ */
+ public ViewDescriptor getViewDescriptor(String viewId) {
+ ViewDescriptor[] viewDescriptors = getViewDescriptors();
+ for (int i = 0; i < viewDescriptors.length; i++) {
+ if (viewDescriptors[i].getId().equalsIgnoreCase(viewId)) {
+ return viewDescriptors[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks for validity of the viewId. For instance,
+ * "CANCEL" is the same as "cancel". So this method normalizes
+ * the view id.
+ * <p>
+ * When the client is doing some logic for some view, it first should normalize it with this method
+ *
+ * @return the normalized viewId (lowercase), when the viewId is <code>null</code> it returns the default view id
+ * */
+ protected final String normalize(String viewId) {
+ if(viewId == null || "".equals(viewId.trim())){
+ return Constants.Request.DEFAULT_VIEW_ID;
+ }
+ for (Iterator i = supportedViewIds.iterator(); i.hasNext();) {
+ String id = (String) i.next();
+ if(id.equalsIgnoreCase(viewId)){
+ return id;
+ }
+ }
+ return viewId.toLowerCase();
+ }
+
+ /**
+ * Gets the names of the i18n message catalogues used for the i18n transformation.
+ * Looks for an rc config property named 'i18n-catalogue'. Defaults to 'global'.
+ * @return i18n catalogue name
+ */
+ protected String[] getI18NCatalogueNames() throws Exception {
+ String[] catalogueNames = getResourceConfigProperties(I18N_CATALOG_PROPERTY);
+ if (catalogueNames == null || catalogueNames.length == 0) {
+ catalogueNames = new String[1];
+ catalogueNames[0] = "global";
+ }
+ return catalogueNames;
+ }
+
+ public long getSize() throws Exception {
+ return -1;
+ }
+
+ public boolean exists() throws Exception {
+ return true;
+ }
+
+ /**
+ * Returns the current continuation associated with the request which is associated with this resource, or if the request does not have a continuation, creates one.
+ */
+ public Continuation getContinuation() {
+ if (getRequest().getParameter(YANEL_CONTINUATION_ID) != null) {
+ String continuationId = getRequest().getParameter(YANEL_CONTINUATION_ID);
+ log.warn("Return existing continuation: " + continuationId);
+ return new Continuation(continuationId);
+ //return new ContinuationSessionImpl(continuationId, getSession(true));
+ //return new ContinuationYarepRepoImpl(continuationId, continuationRepo);
+ } else {
+ Continuation continuation = new Continuation();
+ //Continuation continuation = new ContinuationSessionImpl(getSession(true));
+ //Continuation continuation = new ContinuationYarepRepoImpl(continuationRepo);
+ log.warn("New continuation created: " + continuation.getId());
+ return continuation;
+ }
+ }
+}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ConversationState.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ConversationState.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ConversationState.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,108 @@
+package org.wyona.yanel.impl.resources;
+
+import java.io.Serializable;
+
+import org.wyona.yanel.impl.resources.ResourceAdapter.Usecase;
+
+/**
+ * Conversation state is stored in the session.
+ * During the conversation with the user the model is modified.
+ * However, the model is transient.
+ */
+class ConversationState implements Serializable{
+ private static final long serialVersionUID = 1L;
+
+ //private static final long serialVersionUID = 1724827714145174641L;
+ private Usecase usecase;
+
+ /**
+ * The path to the adapted resource, e.g. Creatable, Modifiable
+ */
+ private String resourcePath;
+ private String refererUrl;
+ private String gotoUrl;
+ private String currentViewId;
+ private String previousViewId;
+ private transient Object model;
+ private boolean invalidated;
+
+ public ConversationState(Object model){
+ this.model = model;
+ }
+
+ public ConversationState(String currentViewId, Object model){
+ this.currentViewId = currentViewId;
+ this.model = model;
+ }
+
+ public String getCurrentScreen() {
+ return currentViewId;
+ }
+
+ public String getPreviousScreen() {
+ return previousViewId;
+ }
+
+ public Object getModel() {
+ return model;
+ }
+
+ public String getRefererUrl() {
+ return refererUrl;
+ }
+
+ void setRefererUrl(String refererUrl) {
+ this.refererUrl = refererUrl;
+ if(gotoUrl == null){
+ this.gotoUrl = this.refererUrl;
+ }
+ }
+
+ public String getGotoUrl() {
+ return gotoUrl;
+ }
+
+ public void setGotoUrl(String gotoUrl) {
+ this.gotoUrl = gotoUrl;
+ }
+
+ /**
+ * Sets the previous and current view id
+ * */
+ void setCurrentScreen(String currentViewId) {
+ this.previousViewId = this.currentViewId;
+ this.currentViewId = currentViewId;
+ }
+
+ void setModel(Object model) {
+ this.model = model;
+ }
+
+ void invalidate(){
+ invalidated = true;
+ }
+
+ /**
+ * Invalidated conversation state means that the state is removed from
+ * the session
+ * */
+ public boolean isInvalidated(){
+ return invalidated;
+ }
+
+ public Usecase getUsecase() {
+ return usecase;
+ }
+
+ public void setUsecase(Usecase usecase) {
+ this.usecase = usecase;
+ }
+
+ public String getResourcePath() {
+ return resourcePath;
+ }
+
+ public void setResourcePath(String resourcePath) {
+ this.resourcePath = resourcePath;
+ }
+}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyAdapterForCUDResource.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyAdapterForCUDResource.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyAdapterForCUDResource.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,451 @@
+package org.wyona.yanel.impl.resources;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.transform.Transformer;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.jelly.JellyContext;
+import org.wyona.yanel.core.Resource;
+import org.wyona.yanel.core.api.attributes.CreatableV3;
+import org.wyona.yanel.core.api.attributes.DeletableV1;
+import org.wyona.yanel.core.api.attributes.ModifiableV3;
+import org.wyona.yanel.core.api.attributes.creatable.ResourceInput;
+import org.wyona.yanel.core.api.attributes.creatable.ResourceInputItem;
+import org.wyona.yanel.core.api.attributes.creatable.ResourceInputItemCollection;
+import org.wyona.yanel.core.attributes.viewable.View;
+import org.wyona.yanel.core.util.ResourceAttributeHelper;
+import org.wyona.yanel.impl.jelly.FileItem;
+import org.wyona.yanel.servlet.communication.HttpRequest;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Adapts a CreatableV3.
+ * <p>
+ * The templates in addition get the following parameters:
+ * <ul>
+ * <li>resourceInput - description of the inputs that creatable resource needs
+ * <li>url.before.conversation - the VIEW can redirect here when the conversation is cancelled
+ * <li>resource.input.collection.action - Decides on what kind of action is taken on collection items. Can be 'add' (default) or 'remove'.
+ * </ul>
+ *
+ * This is quite a generic class. Subclasses may want to pass other parameters to the templates.
+ * */
+public class JellyAdapterForCUDResource extends JellyConversationAdapter {
+
+ private static Logger log = Logger.getLogger(JellyAdapterForCUDResource.class);
+
+ // Collection items are special. Values can be removed or added/set to specific index
+ public static final String REMOVE_ACTION = "remove";
+ public static final String PARAM_RESOURCE_INPUT_COLLECTION_ACTION = "resource.input.collection.action";
+ public static final String PARAM_RESOURCE_INPUT_COLLECTION_INDEX = "resource.input.collection.index";
+
+ // The three parameters must be specified together!
+ public static final String PARAM_RESOURCE_INPUT_COLLECTION_OLD_INDEX = "resource.input.collection.old.index";
+ public static final String PARAM_RESOURCE_INPUT_COLLECTION_NEW_INDEX = "resource.input.collection.new.index";
+ public static final String PARAM_TARGET_RESOURCE_INPUT = "target.resource.input";
+
+ public JellyAdapterForCUDResource() {
+ }
+
+ /**
+ *
+ */
+ public View getView(String viewId)throws Exception{
+ try{
+ ViewDescriptorUsingTemplate viewDescriptor = (ViewDescriptorUsingTemplate)getViewDescriptor(normalize(viewId));
+ if(viewDescriptor == null){
+ throw new IllegalArgumentException("The view descriptor was not found for the given id: '" + viewId + "'");
+ }
+ log.debug("View ID: " + viewDescriptor.getId());
+
+ if (CANCEL_VIEW_ID.equals(viewDescriptor.getId())) {
+ log.warn("Cancel ...");
+ doCancel();
+ destroyConversation();
+ return super.getView(CANCEL_VIEW_ID);
+ }
+
+ // Deal with the input
+ init();
+ ConversationState cs = getConversationState();
+
+ boolean incomingInputIsValid = true;
+ if (cs == null) {
+ throw new IllegalStateException("Conversation state is not available");
+ }
+ String oldIndex = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_OLD_INDEX);
+ String newIndex = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_NEW_INDEX);
+ String [] targetInputs = getParameterAsStringValues(PARAM_TARGET_RESOURCE_INPUT);
+
+ if(oldIndex != null && newIndex != null){
+ try {
+ int o = Integer.parseInt(oldIndex);
+ int n = Integer.parseInt(newIndex);
+
+ ResourceInput ri = (ResourceInput)cs.getModel();
+
+ // Moving input values
+ for (String target : targetInputs) {
+ ResourceInputItem item = ri.getItem(target);
+ if (item instanceof ResourceInputItemCollection) {
+ ResourceInputItemCollection itemAsCollection = (ResourceInputItemCollection) item;
+ itemAsCollection.moveValue(o, n);
+ }
+ }
+ } catch (NumberFormatException e) {
+ log.warn("oldIndex="+oldIndex+", newIndex="+newIndex+": Illegal indexes passed. Operation skiped");
+ }
+ }else{
+ // Fill incoming values and validate. This will also
+ incomingInputIsValid = fillIncomingInput(cs);
+ }
+
+ String nextViewId = viewDescriptor.getId();
+
+
+ if(!incomingInputIsValid){
+ if(!viewDescriptor.isFragment()){
+ // When the screen was not valid go to the previous screen
+ nextViewId = cs.getPreviousScreen();
+ }else{
+ }
+ // Keep the same screen for the fragment
+ cs.setCurrentScreen(cs.getPreviousScreen());
+ }else {
+ if(!viewDescriptor.isFragment()){
+ // Now the current screen is the next view
+ String previous = cs.getPreviousScreen();
+ cs.setCurrentScreen(nextViewId);
+ if (DONE_VIEW_ID.equals(nextViewId)) {
+ // NOTE: assumes that DONE_VIEW_ID is not a fragment
+ try {
+ commit();
+ destroyConversation();
+ } catch (IllegalArgumentException e) {
+ log.warn("Cannot commit", e);
+ //thrown when the ResourceInput is not valid, then go back
+ nextViewId = previous;
+ cs.setCurrentScreen(previous);
+ }
+ }
+ }else{
+ // Keep the previous screen when the input is a fragment
+ cs.setCurrentScreen(cs.getPreviousScreen());
+ }
+ }
+ return super.getView(nextViewId);
+ } catch(Throwable e){
+ // Cancel the conversation when something goes wrong
+ destroyConversation();
+
+ throw new Exception("Due to an exception the request has been canceled. Exception message: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Try to set the resource input values according to what came in HTTP
+ * request.
+ * <p>
+ * NOTE[very important]: this method relies on the fact that HTTP request
+ * has parameters even for checkboxes and select(multiple), which are normally not submitted
+ * when nothing is selected or checked. The "nothing is selected" means that the
+ * parameter is passed but the value is <code>null</code>.
+ * This must be ensured in order to use the adapter correctly, e.g. the simple tag library is
+ * rendering an additional HIDDEN field for this purpose.
+ * @return true when all the incoming input items are valid
+ */
+ private boolean fillIncomingInput(ConversationState cs) throws Exception {
+ ResourceInput ri = (ResourceInput) cs.getModel();
+ String[] itemNames = ri.getItemNames();
+
+ Set incomingParameterNames = new HashSet(getParameters().keySet());
+ // Add incoming names of the inputs that are files
+ if (getEnvironment().getRequest() instanceof HttpRequest) {
+ HttpRequest httpRequest = (HttpRequest) request;
+ if (httpRequest.isMultipartRequest()) {
+ Enumeration params = httpRequest.getFileNames();
+ while (params.hasMoreElements()) {
+ incomingParameterNames.add(params.nextElement());
+ }
+ }
+ }
+ //Now we have all incoming parameter names
+ boolean inputValid = true;
+ for (int i = 0; i < itemNames.length; i++) {
+ String action = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_ACTION);
+ String index = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_INDEX);
+
+ if(!incomingParameterNames.contains(itemNames[i])){
+ // We need to fill only the parameters that were passed
+ continue;
+ }
+
+ // Go further to addValue()/removeValue()/setValue()
+
+ Object paramValue = getParameterAsFileItem(itemNames[i]);
+ if(paramValue == null){
+ paramValue = getParameter(itemNames[i]);
+ }
+
+ if (REMOVE_ACTION.equals(action)){
+ if (ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_COLLECTION) {
+ ResourceInputItemCollection collectionItem = (ResourceInputItemCollection) ri.getItem(itemNames[i]);
+
+ // Remove the value (maybe at the specified index)
+ boolean removed = false;
+ if(paramValue != null){
+ removed = collectionItem.removeValue(paramValue);
+ }
+ if(!removed && index != null){
+ try{
+ int valueIndex = Integer.parseInt(index);
+ collectionItem.removeValue(valueIndex);
+ }catch(NumberFormatException e){
+ log.warn(itemNames[i]+"="+paramValue+": Could not remove the parameter at the index "+index);
+ }
+ }
+ }else{
+ ri.getItem(itemNames[i]).setValue(null);
+ }
+ }else{
+ if(ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_FILE_UPLOAD){
+ FileItem fi = (FileItem)paramValue;
+ if(fi == null || !fi.hasData()){
+ // This will cause the old data (maybe null) to be left and validated
+ inputValid = ri.getItem(itemNames[i]).validate() && inputValid;
+ continue;
+ }
+ }
+
+ if (ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_COLLECTION) {
+ ResourceInputItemCollection collectionItem = (ResourceInputItemCollection) ri.getItem(itemNames[i]);
+ if(index != null){
+ try{
+ int valueIndex = Integer.parseInt(index);
+ collectionItem.addValue(valueIndex, paramValue);
+ }catch(NumberFormatException e){
+ log.warn(itemNames[i]+"="+paramValue+": Could not add the parameter at the index "+index+", appending");
+ collectionItem.addValue(paramValue);
+ }
+ }else{
+ collectionItem.addValue(paramValue);
+ }
+ } else {
+ ri.getItem(itemNames[i]).setValue(paramValue);
+ }
+ }
+ inputValid = ri.getItem(itemNames[i]).validate() && inputValid;
+ }
+
+ return inputValid;
+ }
+
+ /**
+ * @return <code>null</code> when the parameter is not a file upload, otherwise a constructed file item.
+ * */
+ private FileItem getParameterAsFileItem(String parameterName)throws Exception{
+ FileItem fileItem = null;
+ if (getEnvironment().getRequest() instanceof HttpRequest) {
+ HttpRequest httpRequest = (HttpRequest) request;
+ if (httpRequest.isMultipartRequest()) {
+ Enumeration fileParameterNames = httpRequest.getFileNames();
+ while (fileParameterNames.hasMoreElements()) {
+ // create new file item
+ String name = (String) fileParameterNames.nextElement();
+ if (name.equals(parameterName)) {
+ String contentType = httpRequest.getContentType(name);
+ InputStream is = httpRequest.getInputStream(name);
+ fileItem = new FileItem(IOUtils.toByteArray(is), contentType);
+ fileItem.setFileName(httpRequest.getFilesystemName(name));
+ break;
+ }
+ }
+ }
+ }
+ return fileItem;
+ }
+
+ /**
+ *
+ */
+ protected void init() throws Exception {
+ ConversationState cs = getConversationState();
+
+ // try to identify the usecase. It is either in the request or in the conversation
+ Usecase usecase = getUsecase();
+ if (usecase == null) {
+ log.warn("No usecase (neither 'create' nor 'update' nor 'delete') has been specified!");
+ //throw new UnsupportedOperationException("No usecase (neither 'create' nor 'update' nor 'delete') has been specified!");
+ }
+ List<Usecase> supportedUsecases = Arrays.asList(Usecase.values());
+ if (!supportedUsecases.contains(usecase)) {
+ if (cs == null || !supportedUsecases.contains(cs.getUsecase())){
+ throw new UnsupportedOperationException("The following usecase is not supported by this adapter implementation: " + usecase);
+ }
+ usecase = cs.getUsecase();
+ }
+
+ String resourcePath = getAdaptedResourcePathFromConversationState();
+
+ // TODO: For creation one doesn't need necessarily a resource path, but a resource type definition would be sufficient, whereas for update/modify and delete one needs a resource path
+ if(resourcePath == null){
+ String adaptedResourceName = getResourceConfigProperty("adapted-resource-name");
+ String adaptedResourceNamespace = getResourceConfigProperty("adapted-resource-namespace");
+ if (adaptedResourceName != null && adaptedResourceNamespace != null) {
+ log.warn("TODO: Implement initialization of resource from resource type definition: " + adaptedResourceName + ", " + adaptedResourceNamespace);
+ }
+ throw new IllegalStateException("The adapted resource path must be specified in the request or should be available in the conversation state");
+ }
+
+ Object model = null;
+ if (cs == null || cs.getModel() == null) { // This is check is needed because the model is not serializable
+ // Instantiate model, depending on the usecase
+ if (Usecase.create.equals(usecase)) {
+ CreatableV3 cv3 = getAdaptedResourceAsCreatableV3(resourcePath);
+ model = cv3.getResourceInputForCreation();
+ } else if (Usecase.modify.equals(usecase)) {
+ ModifiableV3 mv3 = getAdaptedResourceAsModifiableV3(resourcePath);
+ model = mv3.getResourceInputForModification();
+ } else if (Usecase.remove.equals(usecase)) {
+ DeletableV1 dv1 = getAdaptedResourceAsDeletableV1(resourcePath);
+ model = dv1.getResourceInputForDeletion();
+ } else {
+ log.warn("Could not identify usecase");
+ }
+ } else {
+ model = cs.getModel();
+ }
+
+ initConversation(model, usecase, resourcePath);
+ }
+
+ /**
+ * Validates and calls the create() or modify() or delete() method
+ */
+ public void commit() throws Exception {
+ java.text.DateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSZ");
+ if( log.isDebugEnabled() )
+ log.debug("Start commit " + dateFormat.format(new java.util.Date()));
+ ConversationState cs = getConversationState();
+ String path = cs.getResourcePath();
+ Usecase usecase = cs.getUsecase();
+
+ if (Usecase.create.equals(usecase)) {
+ CreatableV3 cv3 = getAdaptedResourceAsCreatableV3(path);
+ ResourceInput input = (ResourceInput) getConversationState().getModel();
+ if(input.validate()){
+ if( log.isDebugEnabled() )
+ log.debug("Start create " + dateFormat.format(new java.util.Date()));
+ cv3.create(input);
+ if( log.isDebugEnabled() )
+ log.debug("End create " + dateFormat.format(new java.util.Date()));
+ }else{
+ throw new IllegalArgumentException("The input for the adapted resource is not valid");
+ }
+ } else if (Usecase.modify.equals(usecase)) {
+ ModifiableV3 mv3 = getAdaptedResourceAsModifiableV3(path);
+ ResourceInput input = (ResourceInput) getConversationState().getModel();
+ if(input.validate()){
+ mv3.modify(input);
+ }else{
+ throw new IllegalArgumentException("The input for the adapted resource is not valid");
+ }
+ } else if (Usecase.remove.equals(usecase)) {
+ DeletableV1 dv1 = getAdaptedResourceAsDeletableV1(path);
+ ResourceInput input = (ResourceInput) getConversationState().getModel();
+ if(input.validate()){
+ dv1.delete(input);
+ }else{
+ throw new IllegalArgumentException("The input for the adapted resource is not valid");
+ }
+ }
+ if( log.isDebugEnabled() )
+ log.debug("End commit " + dateFormat.format(new java.util.Date()));
+ }
+
+ /**
+ * If the conversation/continuation has been canceled, then react accordingly
+ */
+ private void doCancel() throws Exception {
+ String adaptedResourcePath = getAdaptedResourcePathFromConversationState();
+ log.debug("Adapted resource: " + adaptedResourcePath);
+ Resource adaptedResource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), adaptedResourcePath);
+ if (ResourceAttributeHelper.hasAttributeImplemented(adaptedResource, "Viewable", "2") && ResourceAttributeHelper.hasAttributeImplemented(adaptedResource, "Versionable", "2")) {
+ if (((org.wyona.yanel.core.api.attributes.ViewableV2) adaptedResource).exists()) {
+ ((org.wyona.yanel.core.api.attributes.VersionableV2) adaptedResource).cancelCheckout();
+ } else {
+ log.warn("Resource '" + adaptedResourcePath + "' does not exist!");
+ }
+ } else {
+ log.warn("Resource '" + adaptedResourcePath + "' is not ViewableV2/VersionableV2 and hence checkout cannot be canceled!");
+ }
+ }
+
+ /**
+ * Get adapted resource path. It is either in the request or in the conversation.
+ */
+ private String getAdaptedResourcePathFromConversationState() {
+ String adaptedResourcePath = getAdaptedResourcePath();
+ ConversationState cs = getConversationState();
+ if(adaptedResourcePath == null && cs != null) {
+ adaptedResourcePath = cs.getResourcePath();
+ }
+ return adaptedResourcePath;
+ }
+
+ /**
+ * Check if resource has the interface CreatableV3 implemented
+ */
+ private CreatableV3 getAdaptedResourceAsCreatableV3(String path) throws Exception {
+ Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
+ if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Creatable", "3")) {
+ throw new Exception("The adapted resource (" + resource.getResourceTypeUniversalName() + ", " + path + ") is not of the type '" + CreatableV3.class.getName() + "'");
+ }
+ return (CreatableV3) resource;
+ }
+
+ private ModifiableV3 getAdaptedResourceAsModifiableV3(String path) throws Exception {
+ Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
+ if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Modifiable", "3")) {
+ throw new Exception("The adapted resource is not of the type " + ModifiableV3.class.getName());
+ }
+ return (ModifiableV3) resource;
+ }
+
+ private DeletableV1 getAdaptedResourceAsDeletableV1(String path) throws Exception {
+ Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
+ if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Deletable", "1")) {
+ throw new Exception("The adapted resource is not of the type " + DeletableV1.class.getName());
+ }
+ return (DeletableV1) resource;
+ }
+
+ protected void passParameters(Transformer transformer) throws Exception {
+ super.passParameters(transformer);
+ ConversationState cs = getConversationState();
+ if (cs != null) {
+ transformer.setParameter("resourceInput", cs.getModel());
+ transformer.setParameter("url.before.conversation", cs.getRefererUrl());
+ } else {
+ log.warn("The conversation was not initialized");
+ }
+ }
+
+ protected void passParameters(JellyContext jellyContext) throws Exception {
+ super.passParameters(jellyContext);
+ ConversationState cs = getConversationState();
+ if (cs != null) {
+ jellyContext.setVariable("resourceInput", cs.getModel());
+ jellyContext.setVariable("url.before.conversation", cs.getRefererUrl());
+ } else {
+ log.warn("The conversation was not initialized");
+ }
+ }
+}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyControllerAdapter.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyControllerAdapter.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyControllerAdapter.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,469 @@
+package org.wyona.yanel.impl.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationUtil;
+import org.apache.commons.jelly.JellyContext;
+import org.apache.commons.jelly.XMLOutput;
+import org.apache.xml.resolver.tools.CatalogResolver;
+import org.apache.xml.serializer.Serializer;
+import org.w3c.dom.Document;
+import org.wyona.security.core.api.Identity;
+import org.wyona.yanel.core.Constants;
+import org.wyona.yanel.core.attributes.viewable.View;
+import org.wyona.yanel.core.serialization.SerializerFactory;
+import org.wyona.yanel.core.source.SourceResolver;
+import org.wyona.yanel.core.transformation.I18nTransformer2;
+import org.wyona.yanel.core.transformation.XIncludeTransformer;
+import org.wyona.yanel.core.util.PathUtil;
+import org.wyona.yanel.impl.resources.ViewDescriptorUsingTemplate.TemplateOption;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This resource type takes care only of the custom configuration.
+ * It behaves much like org.wyona.yanel.impl.resources.usecase.UsecaseResource.
+ * In the resource configuration one can specify views and there a Jelly template and arbitrary XSLTs.
+ * <p>
+ * The templates get the following parameters(subclasses may enrich this set):
+ * <ul>
+ * <li> <b>All the parameters from the incoming request</b>
+ * <li>yanel.path.name
+ * <li>yanel.path
+ * <li>yanel.back2realm
+ * <li>yanel.back2context
+ * <li>yanel.global.htdocs
+ * <li>yanel.resource.htdocs
+ * <li>yanel.requested.language
+ * <li>yanel.content.language
+ * <li>client (i.e. user agent)
+ * <li>yanel.username
+ * <li>yanel.toolbar.status
+ * <li>yanel.reserved.prefix
+ * </ul>
+ * <p>
+ * The view description looks as follows:
+ * <pre>
+ * [yanel:custom-config]
+ * [views xmlns="http://www.wyona.org/yanel/rti/1.0"]
+ * [view id="..."]
+ * [template type="JELLY"]...[/template]
+ * [xslt]...[/xslt]*
+ * [mime-type]...[/mime-type]? (e.g. text/html)
+ * [serializer key="..."/]?
+ * [/view]
+ * [screen/]*
+ * ...
+ * [fragment/]*
+ * [/yanel:custom-config]
+ * </pre>
+ * The 'fragment' view hints the view adapters that this is not the user screen but rather a fragment generated by AJAX call or something similar.
+ * The 'screen' view is the synonym of 'view', used to stress that the view is not a fragment
+ * */
+public abstract class JellyControllerAdapter extends ControllerAdapter {
+
+ private static Logger log = Logger.getLogger(JellyControllerAdapter.class);
+
+ // Jelly can be configured to escape the text that it gets from JAVA
+ // This parameter allows to disable escaping.
+ // Accepted values: on, off, true, false, yes, no
+ public static final String ESCAPE_TEXT_IN_JELLY = "escape.text.in.jelly";
+
+ private HashMap<String, ViewDescriptorUsingTemplate> viewDescriptors;
+
+ public JellyControllerAdapter() {
+ super();
+ }
+
+ /**
+ * Checks if the parameter that configures the escaping was passed:
+ * <ul>
+ * <li> As a parameter in the request
+ * <li> As a property in the configuration
+ * </ul>
+ * @return <code>true</code> by default, otherwise depends on the parameter value
+ * */
+ public boolean isEscapeTextEnabled(){
+ String escape = getParameterAsString(ESCAPE_TEXT_IN_JELLY);
+ if(escape == null){
+ try {
+ escape = getConfiguration().getProperty(ESCAPE_TEXT_IN_JELLY);
+ } catch (Exception e) {
+ log.info("Could not detect the property "+ESCAPE_TEXT_IN_JELLY+", will use default value");
+ }
+ }
+
+ if(escape == null){
+ return true;
+ }
+ if("on".equals(escape.trim().toLowerCase()) || "yes".equals(escape.trim().toLowerCase()) || Boolean.valueOf(escape.trim())){
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ *
+ */
+ public ViewDescriptorUsingTemplate[] getViewDescriptors() {
+ if (this.viewDescriptors == null) {
+ try {
+ this.viewDescriptors = new HashMap<String, ViewDescriptorUsingTemplate>();
+
+ // reads views from configuration:
+ if (getConfiguration() != null && getConfiguration().getCustomConfiguration() != null) {
+ Document customConfigDoc = getConfiguration().getCustomConfiguration();
+ Configuration config = ConfigurationUtil.toConfiguration(customConfigDoc.getDocumentElement());
+ Configuration viewsConfig = config.getChild("views");
+
+ {
+ ViewDescriptorUsingTemplate defaultView = null;
+
+ // Collect view definitions
+ Configuration[] viewConfigs = viewsConfig.getChildren("view");
+ for (int i = 0; i < viewConfigs.length; i++) {
+ String id = normalize(viewConfigs[i].getAttribute("id"));
+
+ ViewDescriptorUsingTemplate viewDescriptor = new ViewDescriptorUsingTemplate(id, viewConfigs[i]);
+
+ if(Constants.Request.DEFAULT_VIEW_ID.equals(id)){
+ defaultView = viewDescriptor;
+ }
+
+ this.viewDescriptors.put(id, viewDescriptor);
+ }
+
+ if(defaultView == null){
+ defaultView = new ViewDescriptorUsingTemplate(Constants.Request.DEFAULT_VIEW_ID);
+ defaultView.setMimeType("application/xml");
+ this.viewDescriptors.put(Constants.Request.DEFAULT_VIEW_ID, defaultView);
+ }
+ }
+
+ {
+ // Collect fragment definitions
+ //log.debug("Get all fragments ...");
+ Configuration[] fragmentConfigs = viewsConfig.getChildren("fragment");
+ for (int i = 0; i < fragmentConfigs.length; i++) {
+ String id = normalize(fragmentConfigs[i].getAttribute("id"));
+ if(log.isDebugEnabled()) log.debug("Fragment ID: " + id);
+
+ ViewDescriptorUsingTemplate viewDescriptor = new ViewDescriptorUsingTemplate(id, fragmentConfigs[i], true);
+ if(this.viewDescriptors.keySet().contains(id)){
+ log.warn("The view with ID '" + id + "' will be ignored as the descriptor with such an ID already exists!");
+ }else{
+ this.viewDescriptors.put(id, viewDescriptor);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Views are not properly configured");
+ }
+ }
+
+ return this.viewDescriptors.values().toArray(new ViewDescriptorUsingTemplate[this.viewDescriptors.size()]);
+ }
+
+ /**
+ * Runs the pipeline of templates. You can feed an XML, JELLY or XSLT to the pipe.
+ * Every template will be processed and passed through internationalization and XInclude transformers.
+ * So every subsequent template can react to the expanded(resolved) input.
+ */
+ public View getView(String viewId) throws Exception {
+ View view = new View();
+
+ viewId = normalize(viewId);
+
+ ViewDescriptorUsingTemplate viewDescriptor = (ViewDescriptorUsingTemplate)getViewDescriptor(viewId);
+ view.setMimeType(viewDescriptor.getMimeType());
+
+ try {
+ Serializer serializer = viewDescriptor.getSerializer();
+ Serializer xmlSerializer = SerializerFactory.getSerializer(SerializerFactory.XML);
+
+ InputStream content = null;
+
+ TemplateOption [] templates = viewDescriptor.getTemplates();
+ //TODO: don't serialize on each template, rather pipe content handlers
+ for (int i = 0; i < templates.length; i++) {
+ // The first one is JELLY or XML, then every other is XSLT
+ if(i + 1 == templates.length){
+ // Use configured serializer for the last template only
+ content = processTemplate(content, templates[i], serializer);
+ }else{
+ // In the intermediate steps only XML is produced
+ content = processTemplate(content, templates[i], xmlSerializer);
+ }
+ }
+
+ // write result into view:
+ view.setInputStream(content);
+ } catch(Exception e) {
+ log.error(e + " (" + getPath() + ", " + getRealm() + ")", e);
+ throw new Exception(e);
+ }
+
+ return view;
+ }
+
+ /**
+ * @param content Template may be applied to it, e.g. for transformations
+ * @param template Template
+ * @param serializer Serializer
+ *
+ * @return result of the processing as a stream
+ */
+ protected InputStream processTemplate(InputStream content, TemplateOption template, Serializer serializer) throws Exception{
+ InputStream result = null;
+
+ SourceResolver uriResolver = new SourceResolver(this);
+ CatalogResolver catalogResolver = new CatalogResolver();
+
+ InputStream templateInputStream = getTemplateAsStream(template);
+
+ if(TemplateOption.TYPE_XSLT.equals(template.getType())){
+ // Here after the required template it is
+
+ // create xslt transformer:
+ SAXTransformerFactory tf = (SAXTransformerFactory)TransformerFactory.newInstance();
+ tf.setURIResolver(uriResolver);
+
+ // attach handler:
+ TransformerHandler th = tf.newTransformerHandler(new StreamSource(templateInputStream));
+
+ th.getTransformer().setURIResolver(uriResolver);
+ Transformer transformer = th.getTransformer();
+
+ passParameters(transformer);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutputStream(baos);
+ th.setResult(new SAXResult(serializer.asContentHandler()));
+
+ // create reader:
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+ xmlReader.setEntityResolver(catalogResolver);
+ xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+ xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", th);
+ xmlReader.setContentHandler(th);
+
+ // execute:
+ xmlReader.parse(new InputSource(content));
+
+ result = new ByteArrayInputStream(baos.toByteArray());
+ }else if(TemplateOption.TYPE_JELLY.equals(template.getType())){
+ log.debug("Template type: " + template.getType());
+ JellyContext jellyContext = new JellyContext();
+ passParameters(jellyContext);
+
+ ByteArrayOutputStream jellyResultStream = new ByteArrayOutputStream();
+
+ XMLOutput jellyOutput = XMLOutput.createXMLOutput(jellyResultStream, isEscapeTextEnabled());
+ jellyContext.runScript(new InputSource(templateInputStream), jellyOutput);
+ jellyOutput.flush();
+ result = new ByteArrayInputStream(jellyResultStream.toByteArray());
+ }else{
+ result = templateInputStream;
+ }
+
+ //
+ // Pass the result through i18n and XInclude transformers
+ //
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutputStream(baos);
+
+ // create i18n transformer:
+ I18nTransformer2 i18nTransformer = new I18nTransformer2(getI18NCatalogueNames(), getRequestedLanguage(), getRealm().getDefaultLanguage());
+ i18nTransformer.setEntityResolver(catalogResolver);
+
+
+ // create xinclude transformer:
+ XIncludeTransformer xIncludeTransformer = new XIncludeTransformer();
+ xIncludeTransformer.setResolver(uriResolver);
+
+ // chain everything together (create a pipeline):
+ xIncludeTransformer.setResult(new SAXResult(i18nTransformer));
+ i18nTransformer.setResult(new SAXResult(serializer.asContentHandler()));
+
+ // create reader and execute
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+ xmlReader.setEntityResolver(catalogResolver);
+ xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+
+ xmlReader.setContentHandler(new SAXResult(xIncludeTransformer).getHandler());
+ xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", xIncludeTransformer);
+
+ xmlReader.setContentHandler(new SAXResult(i18nTransformer).getHandler());
+ xmlReader.parse(new InputSource(result));
+
+ result = new ByteArrayInputStream(baos.toByteArray());
+
+ return result;
+ }
+
+ /**
+ * Tries to get the template as input stream. The template can be either accessible through
+ * a URL or simply from the repository.
+ *
+ * @param template TemplateOption containing type and URL
+ */
+ protected InputStream getTemplateAsStream(TemplateOption template)throws Exception{
+ log.debug("Template: " + template.getUrl());
+ InputStream templateInputStream = null;
+ if(!template.getUrl().startsWith("/")){
+ // Resolve the URI into the InputStream
+ Source src = new SourceResolver(this).resolve(template.getUrl(), null);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ TransformerFactory.newInstance().newTransformer().transform(src, new StreamResult(bos));
+ templateInputStream = new ByteArrayInputStream(bos.toByteArray());
+ }else{
+ // Just take the template from the repository node
+ templateInputStream = getRealm().getRepository().getNode(template.getUrl()).getInputStream();
+ }
+ return templateInputStream;
+ }
+
+ /**
+ * Pass parameters to Jelly context (Add more parameters in subclasses if needed)
+ */
+ protected void passParameters(JellyContext jellyContext) throws Exception{
+ // Attach all parameters that came with the request, such that templates can make use of them.
+ // NOTE: all parameter values will be of type String. In XSLT: <param name="p" value="'actual_value'"/>
+ for (Iterator i = getParameters().entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ jellyContext.setVariable(String.valueOf(entry.getKey()), entry.getValue());
+ }
+
+ // Set general parameters
+ jellyContext.setVariable("resource", this); // TODO: Somehow this doesn't work!
+
+ jellyContext.setVariable("continuation.id", getContinuation().getId());
+ jellyContext.setVariable("continuation.id.key", YANEL_CONTINUATION_ID);
+
+ jellyContext.setVariable("yanel.path.name", org.wyona.commons.io.PathUtil.getName(getPath()));
+ jellyContext.setVariable("yanel.path", getPath());
+
+ jellyContext.setVariable("yanel.back2realm", PathUtil.backToRealm(getPath()));
+ jellyContext.setVariable("yanel.back2context", PathUtil.backToContext(realm, getPath()));
+
+ jellyContext.setVariable("yanel.global.htdocs", PathUtil.getGlobalHtdocsPath(this));
+ jellyContext.setVariable("yanel.resource.htdocs", PathUtil.getResourcesHtdocsPath(this));
+
+ jellyContext.setVariable("yanel.requested.language", getRequestedLanguage());
+ jellyContext.setVariable("yanel.content.language", getContentLanguage());
+
+ String userAgent = getEnvironment().getRequest().getHeader("User-Agent");
+ String client = getClient(userAgent);
+ if (client != null) jellyContext.setVariable("client", client);
+
+ // username
+ String username = getUsername();
+ if (username != null) jellyContext.setVariable("yanel.username", username);
+
+ // Add toolbar status
+ String toolbarStatus = getToolbarStatus();
+ if (toolbarStatus != null) jellyContext.setVariable("yanel.toolbar.status", toolbarStatus);
+
+ jellyContext.setVariable("yanel.reserved.prefix", getYanel().getReservedPrefix());
+ }
+
+ /**
+ * Add more parameters in subclasses if needed
+ * */
+ protected void passParameters(Transformer transformer) throws Exception{
+ // Attach all parameters that came with the request. Templates can make use of them.
+ // NOTE: all parameter values will be of type String. In XSLT: <param name="p" value="'actual_value'"/>
+ for (Iterator i = getParameters().entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ if (entry.getValue() instanceof String) {
+ String value = (String) entry.getValue();
+ transformer.setParameter(String.valueOf(entry.getKey()), value);
+ } else if(entry.getValue() instanceof String[]){
+ // values separated by a space
+ String separator = " ";
+
+ StringBuffer finalValue = new StringBuffer();
+ String [] values = (String[]) entry.getValue();
+ for (int j = 0; j < values.length; j++) {
+ finalValue.append(values[j]);
+ if(j + 1 != values.length){
+ finalValue.append(separator);
+ }
+ }
+ transformer.setParameter(String.valueOf(entry.getKey()), finalValue);
+ } else{
+ // Never happens
+ }
+ }
+
+ // Set general parameters
+ transformer.setParameter("yanel.path.name", org.wyona.commons.io.PathUtil.getName(getPath()));
+ transformer.setParameter("yanel.path", getPath());
+
+ transformer.setParameter("yanel.back2realm", PathUtil.backToRealm(getPath()));
+ transformer.setParameter("yanel.back2context", PathUtil.backToContext(realm, getPath()));
+
+ transformer.setParameter("yanel.global.htdocs", PathUtil.getGlobalHtdocsPath(this));
+ transformer.setParameter("yanel.resource.htdocs", PathUtil.getResourcesHtdocsPath(this));
+
+ transformer.setParameter("yanel.requested.language", getRequestedLanguage());
+ transformer.setParameter("yanel.content.language", getContentLanguage());
+
+ String userAgent = getEnvironment().getRequest().getHeader("User-Agent");
+ String client = getClient(userAgent);
+ if (client != null) transformer.setParameter("client", client);
+
+ // username
+ String username = getUsername();
+ if (username != null) transformer.setParameter("yanel.username", username);
+
+ // Add toolbar status
+ String toolbarStatus = getToolbarStatus();
+ if (toolbarStatus != null) transformer.setParameter("yanel.toolbar.status", toolbarStatus);
+
+ transformer.setParameter("yanel.reserved.prefix", getYanel().getReservedPrefix());
+ }
+
+ protected final String getUsername() {
+ Identity identity = getEnvironment().getIdentity();
+ if (identity != null) return identity.getUsername();
+ return null;
+ }
+
+ protected final String getClient(String userAgent) {
+ if (userAgent.indexOf("Firefox") > 0) {
+ return "firefox";
+ } else if (userAgent.indexOf("MSIE") > 0) {
+ return "msie";
+ } else {
+ log.warn("Client could not be recognized: " + userAgent);
+ return null;
+ }
+ }
+
+ protected final String getToolbarStatus() {
+ // TODO: Use YanelServlet.TOOLBAR_KEY instead "toolbar"!
+ return (String) getEnvironment().getRequest().getSession(true).getAttribute("toolbar");
+ }
+
+}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyConversationAdapter.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyConversationAdapter.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyConversationAdapter.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,139 @@
+package org.wyona.yanel.impl.resources;
+
+import javax.xml.transform.Transformer;
+
+import org.apache.commons.jelly.JellyContext;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Deals with conversations.
+ * A conversation is mapped to a path.
+ * */
+//TODO: is path ok as a key of the conversation state
+//TODO[high]: conversation state object must be synchronized, because it is put into the
+//session and may be accessed by different browser instances
+abstract class JellyConversationAdapter extends JellyControllerAdapter {
+
+ private static Logger log = Logger.getLogger(JellyConversationAdapter.class);
+
+ /**
+ * This is the view where the user should get after all the activities are finished.
+ * */
+ protected static final String DONE_VIEW_ID = "done";
+
+ /**
+ * This is the view where the user should get when the conversation is canceled.
+ * */
+ protected static final String CANCEL_VIEW_ID = "cancel";
+
+ /**
+ * This parameter is useful when the referrer url is not enough.
+ * For example, the page may needs to go to some page (other than a referrer) after the conversation has been completed.
+ * */
+ public static final String PARAM_GOTO_URL = "goto.url";
+
+ private ConversationState conversationState;
+
+ public JellyConversationAdapter() {
+ super();
+ supportedViewIds.add(CANCEL_VIEW_ID);
+ supportedViewIds.add(DONE_VIEW_ID);
+ }
+
+ /**
+ * Initialize or keep up to date the conversation state
+ */
+ protected abstract void init() throws Exception;
+
+ /**
+ * In this method the conversation should be commited. Data may need to be validated befor commit.
+ */
+ public abstract void commit()throws Exception;
+
+
+ /**
+ * Returns the conversation state object. It may be available in the session
+ * or invalidated (no more in the session).
+ * @return - <code>null</code> when called before initConversation(), in other cases the conversation state object (maybe invalidated already)
+ * */
+ protected final ConversationState getConversationState(){
+ if(conversationState == null){
+ conversationState = (ConversationState)getEnvironment().getRequest().getSession(true).getAttribute(getPath());
+ }
+ return conversationState;
+ }
+
+ /**
+ * Checks if the conversation is already in the session and creates it if necessary
+ * */
+ protected final ConversationState initConversation(Object model, Usecase usecase, String resourcePath){
+ log.warn("Continuation: " + getContinuation().getId());
+
+ ConversationState cs = new ConversationState(model);
+ cs.setUsecase(usecase);
+ cs.setResourcePath(resourcePath);
+
+ ConversationState current = getConversationState();
+ if (current != null && !current.isInvalidated()) {
+ // Refresh the properties, because they are changing during the conversation
+ current.setModel(cs.getModel());
+ current.setCurrentScreen(cs.getCurrentScreen());
+ } else {
+ cs.setRefererUrl(getEnvironment().getRequest().getHeader("Referer"));
+ // The new conversation state becomes current
+ current = cs;
+ }
+
+ String gotoUrl = getParameterAsString(PARAM_GOTO_URL);
+ if(gotoUrl != null){
+ current.setGotoUrl(gotoUrl);
+ }
+
+ log.warn("Attach conversation state for usecase '" + current.getUsecase() + "' to session with id '" + getPath() + "'");
+ getEnvironment().getRequest().getSession(true).setAttribute(getPath(), current);
+ return current;
+ }
+
+ /**
+ * Removes the conversation state from the session and invalidates the conversation state object.
+ * */
+ protected final void destroyConversation(){
+ ConversationState current = getConversationState();
+ if(current != null){
+ current.invalidate();
+ }
+ getEnvironment().getRequest().getSession(true).removeAttribute(getPath());
+ }
+
+ /**
+ * No additional parameters passed. Subclasses should consult conversation state for additional parameters
+ */
+ protected void passParameters(JellyContext jellyContext) throws Exception {
+ super.passParameters(jellyContext);
+ ConversationState cs = getConversationState();
+ if (cs != null) {
+ if(cs.getGotoUrl() != null){
+ jellyContext.setVariable(PARAM_GOTO_URL, cs.getGotoUrl());
+ }
+ } else {
+ log.warn("The conversation was not initialized");
+ }
+
+ }
+
+ /**
+ * No additional parameters passed. Subclasses should consult conversation state for additional parameters
+ */
+ protected void passParameters(Transformer transformer) throws Exception {
+ super.passParameters(transformer);
+ ConversationState cs = getConversationState();
+ if (cs != null) {
+ if(cs.getGotoUrl() != null){
+ transformer.setParameter(PARAM_GOTO_URL, cs.getGotoUrl());
+ }
+ } else {
+ log.warn("The conversation was not initialized");
+ }
+ }
+}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ResourceAdapter.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ResourceAdapter.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ResourceAdapter.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,51 @@
+package org.wyona.yanel.impl.resources;
+
+
+/**
+ * The adapter knows how to adapt resources. It works as a facade
+ * for adapted resource that are implementing some specific functionality.
+ * E.g. a resource can be creatable, deletable, modifiable...
+ * <p>
+ * Normally the adapter should be instantiated by Yanel servlet
+ * */
+public interface ResourceAdapter{
+ public static final String PARAM_ADAPTED_RESOURCE_PATH = "adapted.resource.path";
+
+ public static enum Usecase{
+ /**
+ * If this usecase is specified, then a resource of the specified type will create something.
+ */
+ create,
+ /**
+ * If this usecase is specified, then a resource of the specified type will modify something.
+ */
+ modify,
+ /**
+ * If this usecase is specified, then a resource of the specified type will delete something.
+ */
+ //TODO: the value must be "delete", but YanelServlet does not allow that?
+ remove;
+
+ /**
+ * Create the usecase out of the given parameter ignoring the case of the string.
+ * Behaves much like simple valueOf
+ * @return null when the parameter is null, otherwise tries to create the usecase.
+ * */
+ public static Usecase caseInsensitiveValueOf(String usecase){
+ if(usecase == null){
+ return null;
+ }
+
+ return Usecase.valueOf(usecase.toLowerCase());
+ }
+ }
+
+ public String getAdaptedResourcePath();
+ public void setAdaptedResourcePath(String adaptedResourcePath);
+
+ /**
+ * The adapter knows how to adapt the resource for specific usecases
+ * */
+ public Usecase getUsecase();
+ public void setUsecase(Usecase usecase);
+}
Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ViewDescriptorUsingTemplate.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ViewDescriptorUsingTemplate.java (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ViewDescriptorUsingTemplate.java 2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,195 @@
+package org.wyona.yanel.impl.resources;
+
+import java.util.Enumeration;
+import java.util.Properties;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.xml.serializer.Serializer;
+import org.wyona.commons.io.MimeTypeUtil;
+import org.wyona.yanel.core.attributes.viewable.ViewDescriptor;
+import org.wyona.yanel.core.serialization.SerializerFactory;
+
+/**
+ * A view is configured with a template and a set of XSLTs:
+ * <pre>
+ * [view id="x"]
+ * [template type="JELLY"]...location...[/template]
+ * [xslt]_location_[/xslt]
+ * ...
+ * [xslt]_location_[/xslt]
+ * [mime-type]...(test/html)...[mime-type]
+ * [serializer key="...(XHTML_STRICT)..."]
+ * ... serializer props...
+ * [/serializer]
+ *
+ * [/view]
+ * </pred>
+ * */
+public class ViewDescriptorUsingTemplate extends ViewDescriptor {
+
+ /**
+ *
+ */
+ public static class TemplateOption{
+ public static final String TYPE_JELLY = "JELLY";
+ public static final String TYPE_XSLT = "XSLT";
+ /**
+ * This is to say that it is raw XML
+ */
+ public static final String TYPE_XML = "XML";
+
+ private String type;
+ private String url;
+
+ public TemplateOption(String type, String url) {
+ this.type = type;
+ this.url = url;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+ }
+
+ private boolean fragment = false;
+
+ protected TemplateOption [] templates = new TemplateOption[0];
+
+ protected String serializerKey = SerializerFactory.XHTML_STRICT_KEY;
+ protected Properties serializerProperties = new Properties();
+
+ protected ViewDescriptorUsingTemplate(String id){
+ super(id);
+ }
+
+ public ViewDescriptorUsingTemplate(String id, Configuration viewDefinition) throws ConfigurationException{
+ this(id, viewDefinition, false);
+ }
+
+ public ViewDescriptorUsingTemplate(String id, Configuration viewDefinition, boolean isFragment) throws ConfigurationException{
+ super(id);
+ configure(viewDefinition);
+ this.fragment = isFragment;
+ }
+
+ /**
+ * Defaults:
+ * <p>
+ * mime-type : text/html<br>
+ * serializer: XHTML_STRICT<br>
+ * serializer props: empty
+ */
+ protected void configure(Configuration config) throws ConfigurationException {
+ Configuration[] templateConfigs = config.getChildren("template");
+ if(templateConfigs.length == 0){
+ throw new ConfigurationException("A template is not specified");
+ }
+
+ Configuration[] xsltConfigs = config.getChildren("xslt");
+
+ templates = new TemplateOption[1 + xsltConfigs.length];
+
+ templates[0] = new TemplateOption(templateConfigs[0].getAttribute("type"), templateConfigs[0].getValue());
+ for (int i = 0; i < xsltConfigs.length; i++) {
+ templates[i+1] = new TemplateOption(TemplateOption.TYPE_XSLT, xsltConfigs[i].getValue());
+ }
+
+ Configuration mimeTypeConfig = config.getChild("mime-type", false);
+ if (mimeTypeConfig != null) {
+ setMimeType(mimeTypeConfig.getValue());
+ }else{
+ setMimeType("text/html");
+ }
+
+ Configuration serializerConfig = config.getChild("serializer", false);
+ if (serializerConfig != null) {
+ serializerKey = (serializerConfig.getAttribute("key") == null ? serializerKey : serializerConfig.getAttribute("key"));
+ serializerProperties = new Properties();
+ Configuration propertyConfig = serializerConfig.getChild("omit-xml-declaration", false);
+ if (propertyConfig != null) {
+ serializerProperties.setProperty("omit-xml-declaration", propertyConfig.getValue());
+ }
+ propertyConfig = serializerConfig.getChild("doctype-public", false);
+ if (propertyConfig != null) {
+ serializerProperties.setProperty("doctype-public", propertyConfig.getValue());
+ }
+ propertyConfig = serializerConfig.getChild("doctype-system", false);
+ if (propertyConfig != null) {
+ serializerProperties.setProperty("doctype-sytem", propertyConfig.getValue());
+ }
+ }
+ }
+
+ public String getSerializerKey() {
+ return serializerKey;
+ }
+
+ public void setSerializerKey(String serializerKey) {
+ this.serializerKey = serializerKey;
+ }
+
+ public Properties getSerializerProperties() {
+ return serializerProperties;
+ }
+
+ public void setSerializerProperties(Properties serializerProperties) {
+ this.serializerProperties = serializerProperties;
+ }
+
+ public TemplateOption[] getTemplates() {
+ return templates;
+ }
+
+ /**
+ * @return <code>true</code> when the view represents a fragment, not a screen.
+ * For instance, a fragment view is useful when doing AJAX call
+ * */
+ public boolean isFragment(){
+ return fragment;
+ }
+
+ /**
+ * Creates an html or xml serializer for this view descriptor
+ */
+ public Serializer getSerializer() throws Exception {
+ Serializer serializer = null;
+ String serializerKey = getSerializerKey();
+ if (serializerKey != null) {
+ serializer = SerializerFactory.getSerializer(serializerKey);
+ if (serializer == null) {
+ throw new Exception("could not create serializer for key: " + serializerKey);
+ }
+ } else {
+ String mimeType = getMimeType();
+
+ if (MimeTypeUtil.isHTML(mimeType) && !MimeTypeUtil.isXML(mimeType)) {
+ serializer = SerializerFactory.getSerializer(SerializerFactory.HTML_TRANSITIONAL);
+ } else if (MimeTypeUtil.isHTML(mimeType) && MimeTypeUtil.isXML(mimeType)){
+ serializer = SerializerFactory.getSerializer(SerializerFactory.XHTML_STRICT);
+ } else if (MimeTypeUtil.isXML(mimeType)) {
+ serializer = SerializerFactory.getSerializer(SerializerFactory.XML);
+ } else if (MimeTypeUtil.isTextual(mimeType)) {
+ serializer = SerializerFactory.getSerializer(SerializerFactory.TEXT);
+ } else{
+ serializer = SerializerFactory.getSerializer(SerializerFactory.XHTML_STRICT);
+ }
+ }
+ // allow to override xml declaration and doctype:
+ Properties properties = getSerializerProperties();
+ if (properties != null) {
+ Enumeration propNames = properties.propertyNames();
+ while (propNames.hasMoreElements()) {
+ String name = (String)propNames.nextElement();
+ String value = properties.getProperty(name);
+
+ serializer.getOutputFormat().setProperty(name, value);
+ }
+ }
+ return serializer;
+ }
+}
More information about the Yanel-commits
mailing list