| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.lenya.cms.usecase; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.Contextualizable; |
| import org.apache.avalon.framework.logger.AbstractLogEnabled; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.cocoon.components.ContextHelper; |
| import org.apache.cocoon.environment.Request; |
| import org.apache.cocoon.servlet.multipart.Part; |
| import org.apache.lenya.cms.publication.DocumentFactory; |
| import org.apache.lenya.cms.publication.DocumentUtil; |
| import org.apache.lenya.cms.repository.Node; |
| import org.apache.lenya.cms.repository.RepositoryException; |
| import org.apache.lenya.cms.repository.RepositoryUtil; |
| import org.apache.lenya.cms.repository.Session; |
| import org.apache.lenya.transaction.ConcurrentModificationException; |
| import org.apache.lenya.transaction.LockException; |
| import org.apache.lenya.transaction.TransactionLock; |
| |
| /** |
| * Abstract usecase implementation. |
| * |
| * @version $Id$ |
| */ |
| public class AbstractUsecase extends AbstractLogEnabled implements Usecase, Configurable, |
| Contextualizable, Serviceable, Initializable { |
| |
| protected static final String EVENT_CHECK_POSTCONDITIONS = "checkPostconditions"; |
| |
| protected static final String EVENT_EXECUTE = "execute"; |
| |
| protected static final String EVENT_CHECK_PRECONDITIONS = "checkPreconditions"; |
| |
| protected static final String EVENT_CHECK_EXECUTION_CONDITIONS = "checkExecutionConditions"; |
| |
| protected static final String ERROR_OBJECTS_CHECKED_OUT = "objects-checked-out"; |
| |
| protected static final StateMachine.Transition[] TRANSITIONS = { |
| new StateMachine.Transition("start", "preChecked", EVENT_CHECK_PRECONDITIONS), |
| new StateMachine.Transition("preChecked", "preChecked", EVENT_CHECK_PRECONDITIONS), |
| new StateMachine.Transition("preChecked", "nodesLocked", "lockInvolvedObjects"), |
| new StateMachine.Transition("nodesLocked", "execChecked", |
| EVENT_CHECK_EXECUTION_CONDITIONS), |
| new StateMachine.Transition("execChecked", "execChecked", |
| EVENT_CHECK_EXECUTION_CONDITIONS), |
| new StateMachine.Transition("nodesLocked", "preChecked", EVENT_CHECK_PRECONDITIONS), |
| new StateMachine.Transition("execChecked", "executed", EVENT_EXECUTE), |
| new StateMachine.Transition("executed", "postChecked", EVENT_CHECK_POSTCONDITIONS) }; |
| |
| protected static final StateMachine.Model MODEL = new StateMachine.Model("start", TRANSITIONS); |
| |
| protected static final String PARAMETER_STATE_MACHINE = "private.stateMachine"; |
| protected static final String PARAMETER_SESSION = "private.session"; |
| protected static final String PARAMETER_FACTORY = "private.factory"; |
| protected static final String PARAMETER_CHECKOUT_RESTRICTED_TO_SESSION = "checkoutRestrictedToSession"; |
| |
| protected static final String PARAMETERS_INITIALIZED = "private.parametersInitialized"; |
| |
| /** |
| * Override to initialize parameters. |
| */ |
| protected void initParameters() { |
| } |
| |
| /** |
| * Advance the usecase state machine to the next state. This method has to be called at the end |
| * of the corresponding method to ensure that the subsequent methods can only be invoked if |
| * nothing went wrong. |
| * @param event The vent to invoke. |
| */ |
| protected void advanceState(String event) { |
| getStateMachine().invoke(event); |
| } |
| |
| protected StateMachine getStateMachine() { |
| StateMachine machine = (StateMachine) getParameter(PARAMETER_STATE_MACHINE); |
| return machine; |
| } |
| |
| protected void checkEvent(String event) { |
| getStateMachine().checkEvent(event); |
| } |
| |
| protected String SOURCE_URL = "private.sourceUrl"; |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#getSourceURL() We don't use getParameterAsString() |
| * because this will typically cause stack overflows or NPEs in connection with |
| * initParameters(). |
| */ |
| public String getSourceURL() { |
| return (String) this.parameters.get(SOURCE_URL); |
| } |
| |
| /** |
| * Returns the context. |
| * @return A context. |
| */ |
| protected Context getContext() { |
| return this.context; |
| } |
| |
| /** |
| * Determine if the usecase has error messages. Provides a way of checking for errors without |
| * actually retrieving them. |
| * @return true if the usecase resulted in error messages. |
| */ |
| public boolean hasErrors() { |
| boolean ret = false; |
| if (this.errorMessages != null) |
| ret = !this.errorMessages.isEmpty(); |
| |
| if (getLogger().isDebugEnabled()) |
| getLogger().debug("AbstractUsecase::hasErrors() called, returning " + ret); |
| |
| return ret; |
| } |
| |
| /** |
| * Determine if the usecase has info messages. Provides a way of checking for info messages |
| * without actually retrieving them. |
| * @return true if the usecase resulted in info messages being generated. |
| */ |
| public boolean hasInfoMessages() { |
| boolean ret = false; |
| if (this.infoMessages != null) |
| ret = !this.infoMessages.isEmpty(); |
| return ret; |
| } |
| |
| /** |
| * Checks if the operation can be executed and returns the error messages. Error messages |
| * prevent the operation from being executed. |
| * @return A boolean value. |
| */ |
| public List getErrorMessages() { |
| return Collections.unmodifiableList(new ArrayList(this.errorMessages)); |
| } |
| |
| /** |
| * Returns the information messages to show on the confirmation screen. |
| * @return An array of strings. Info messages do not prevent the operation from being executed. |
| */ |
| public List getInfoMessages() { |
| return Collections.unmodifiableList(new ArrayList(this.infoMessages)); |
| } |
| |
| private List errorMessages = new ArrayList(); |
| private List infoMessages = new ArrayList(); |
| |
| /** |
| * Adds an error message. |
| * @param message The message. |
| */ |
| public void addErrorMessage(String message) { |
| this.errorMessages.add(new UsecaseMessage(message)); |
| } |
| |
| /** |
| * Adds an error message. |
| * @param message The message. |
| * @param _params parameters |
| */ |
| public void addErrorMessage(String message, String[] _params) { |
| this.errorMessages.add(new UsecaseMessage(message, _params)); |
| } |
| |
| /** |
| * Adds an error message. |
| * @param messages The messages. |
| */ |
| public void addErrorMessages(String[] messages) { |
| for (int i = 0; i < messages.length; i++) { |
| addErrorMessage(messages[i]); |
| } |
| } |
| |
| /** |
| * Adds an info message. |
| * @param message The message. |
| * @param _params parameters |
| */ |
| public void addInfoMessage(String message, String[] _params) { |
| this.infoMessages.add(new UsecaseMessage(message, _params)); |
| } |
| |
| /** |
| * Adds an info message. |
| * @param message The message. |
| */ |
| public void addInfoMessage(String message) { |
| this.infoMessages.add(new UsecaseMessage(message)); |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#checkExecutionConditions() |
| */ |
| public final void checkExecutionConditions() throws UsecaseException { |
| checkEvent(EVENT_CHECK_EXECUTION_CONDITIONS); |
| try { |
| clearErrorMessages(); |
| clearInfoMessages(); |
| doCheckExecutionConditions(); |
| dumpErrorMessages(); |
| } catch (Exception e) { |
| getLogger().error(e.getMessage(), e); |
| addErrorMessage(e.getMessage() + " - Please consult the logfiles."); |
| if (getLogger().isDebugEnabled()) { |
| throw new UsecaseException(e); |
| } |
| } |
| if (!hasErrors()) { |
| advanceState(EVENT_CHECK_EXECUTION_CONDITIONS); |
| } |
| } |
| |
| /** |
| * Checks the execution conditions. |
| * @throws Exception if an error occurs. |
| */ |
| protected void doCheckExecutionConditions() throws Exception { |
| // do nothing |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#checkPreconditions() |
| */ |
| public final void checkPreconditions() throws UsecaseException { |
| checkEvent(EVENT_CHECK_PRECONDITIONS); |
| try { |
| clearErrorMessages(); |
| clearInfoMessages(); |
| |
| Node[] nodes = getNodesToLock(); |
| if (!canCheckOut(nodes)) { |
| addErrorMessage(ERROR_OBJECTS_CHECKED_OUT); |
| } |
| doCheckPreconditions(); |
| |
| List _errorMessages = getErrorMessages(); |
| for (int i = 0; i < _errorMessages.size(); i++) { |
| getLogger().info(_errorMessages.get(i).toString()); |
| } |
| } catch (Exception e) { |
| getLogger().error(e.getMessage(), e); |
| addErrorMessage(e.getMessage() + " - Please consult the logfiles."); |
| if (getLogger().isDebugEnabled()) { |
| throw new UsecaseException(e); |
| } |
| } |
| if (!hasErrors()) { |
| advanceState(EVENT_CHECK_PRECONDITIONS); |
| } |
| } |
| |
| /** |
| * Checks the preconditions. |
| * @throws Exception if an error occurs. |
| */ |
| protected void doCheckPreconditions() throws Exception { |
| // do nothing |
| } |
| |
| /** |
| * Clears the error messages. |
| */ |
| protected void clearErrorMessages() { |
| this.errorMessages.clear(); |
| } |
| |
| /** |
| * Clears the info messages. |
| */ |
| protected void clearInfoMessages() { |
| this.infoMessages.clear(); |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#execute() |
| */ |
| public final void execute() throws UsecaseException { |
| checkEvent(EVENT_EXECUTE); |
| Exception exception = null; |
| try { |
| clearErrorMessages(); |
| clearInfoMessages(); |
| doExecute(); |
| dumpErrorMessages(); |
| } catch (LockException e) { |
| exception = e; |
| addErrorMessage("The operation could not be completed because an involved object was changed by another user."); |
| } catch (Exception e) { |
| exception = e; |
| getLogger().error(e.getMessage(), e); |
| addErrorMessage(e.getMessage() + " - Please consult the logfiles."); |
| throw new UsecaseException(e); |
| } finally { |
| try { |
| if (this.commitEnabled && getErrorMessages().isEmpty() && exception == null) { |
| getSession().commit(); |
| } else { |
| getSession().rollback(); |
| } |
| } catch (ConcurrentModificationException e) { |
| getLogger() |
| .error("Could not commit usecase [" + getName() + "]: " + e.getMessage()); |
| addErrorMessage(e.getMessage()); |
| } catch (Exception e1) { |
| getLogger().error("Could not commit/rollback usecase [" + getName() + "]: ", e1); |
| addErrorMessage("Exception during commit or rollback: " + e1.getMessage() |
| + " (see logfiles for details)"); |
| } |
| } |
| if (!hasErrors()) { |
| advanceState(EVENT_EXECUTE); |
| } |
| } |
| |
| /** |
| * Dumps the error messages to the log. |
| */ |
| protected void dumpErrorMessages() { |
| List _errorMessages = getErrorMessages(); |
| for (int i = 0; i < _errorMessages.size(); i++) { |
| getLogger().error(_errorMessages.get(i).toString()); |
| } |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#checkPostconditions() |
| */ |
| public void checkPostconditions() throws UsecaseException { |
| checkEvent(EVENT_CHECK_POSTCONDITIONS); |
| try { |
| clearErrorMessages(); |
| clearInfoMessages(); |
| doCheckPostconditions(); |
| dumpErrorMessages(); |
| } catch (Exception e) { |
| getLogger().error(e.getMessage(), e); |
| addErrorMessage(e.getMessage() + " - Please consult the logfiles."); |
| if (getLogger().isDebugEnabled()) { |
| throw new UsecaseException(e); |
| } |
| } |
| if (!hasErrors()) { |
| advanceState(EVENT_CHECK_POSTCONDITIONS); |
| } |
| } |
| |
| /** |
| * Checks the post conditions. |
| * @throws Exception if an error occured. |
| */ |
| protected void doCheckPostconditions() throws Exception { |
| // do nothing |
| } |
| |
| /** |
| * Executes the operation. |
| * @throws Exception when something went wrong. |
| */ |
| protected void doExecute() throws Exception { |
| // do nothing |
| } |
| |
| private Map parameters = new HashMap(); |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#setParameter(java.lang.String, java.lang.Object) |
| */ |
| public void setParameter(String name, Object value) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("Setting parameter [" + name + "] = [" + value + "]"); |
| } |
| this.parameters.put(name, value); |
| // set any exit parameters that are missing values |
| if (this.exitUsecaseParameters.containsKey(name) |
| && this.exitUsecaseParameters.get(name) == null) { |
| setExitParameter(name, value.toString()); |
| } |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#getParameter(java.lang.String) |
| */ |
| public Object getParameter(String name) { |
| if (!this.parameters.containsKey(name)) { |
| initializeParametersIfNotDone(); |
| } |
| return this.parameters.get(name); |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#getParameter(java.lang.String, java.lang.Object) |
| */ |
| public Object getParameter(String name, Object defaultValue) { |
| Object value = getParameter(name); |
| if (value == null) { |
| value = defaultValue; |
| } |
| return value; |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#getParameterAsString(java.lang.String) |
| */ |
| public String getParameterAsString(String name) { |
| String valueString = null; |
| Object value = getParameter(name); |
| if (value != null) { |
| valueString = value.toString(); |
| } |
| return valueString; |
| } |
| |
| /** |
| * Returns a parameter as string. If the parameter does not exist, a default value is returned. |
| * @param name The parameter name. |
| * @param defaultValue The default value. |
| * @return A string. |
| */ |
| public String getParameterAsString(String name, String defaultValue) { |
| String valueString = defaultValue; |
| Object value = getParameter(name); |
| if (value != null) { |
| valueString = value.toString(); |
| } |
| return valueString; |
| } |
| |
| /** |
| * Returns a parameter as integer. If the parameter does not exist, a default value is returned. |
| * @param name The parameter name. |
| * @param defaultValue The default value. |
| * @return An integer. |
| */ |
| public int getParameterAsInteger(String name, int defaultValue) { |
| int valueInt = defaultValue; |
| Object value = getParameter(name); |
| if (value != null) { |
| valueInt = Integer.valueOf(value.toString()).intValue(); |
| } |
| return valueInt; |
| } |
| |
| /** |
| * Returns a parameter as boolean. If the parameter does not exist, a default value is returned. |
| * @param name The parameter name. |
| * @param defaultValue The default value. |
| * @return A boolean value.. |
| */ |
| public boolean getParameterAsBoolean(String name, boolean defaultValue) { |
| boolean valueBoolean = defaultValue; |
| Object value = getParameter(name); |
| if (value != null) { |
| if (value instanceof String) { |
| valueBoolean = Boolean.valueOf((String) value).booleanValue(); |
| } else if (value instanceof Boolean) { |
| valueBoolean = ((Boolean) value).booleanValue(); |
| } else { |
| throw new IllegalArgumentException("Cannot get boolean value of parameter [" + name |
| + "] (class " + value.getClass().getName() + ")"); |
| } |
| } |
| return valueBoolean; |
| } |
| |
| /** |
| * Return a map of all parameters |
| * @return the map |
| */ |
| public Map getParameters() { |
| initializeParametersIfNotDone(); |
| return Collections.unmodifiableMap(this.parameters); |
| } |
| |
| /** |
| * Returns one of the strings "true" or "false" depending on whether the corresponding checkbox |
| * was checked. |
| * @param name The parameter name. |
| * @return A string. |
| */ |
| public String getBooleanCheckboxParameter(String name) { |
| String value = "false"; |
| if (getParameter(name) != null && getParameter(name).equals("on")) { |
| value = "true"; |
| } |
| return value; |
| } |
| |
| private String EXIT_URI = "lenya.exitUri"; |
| private String DEFAULT_TARGET_URL = "private.defaultTargetUrl"; |
| |
| /** |
| * Sets the default target URL which should be used if no explicit target URL is set. |
| * @param url A URL string. |
| */ |
| protected void setDefaultTargetURL(String url) { |
| setParameter(DEFAULT_TARGET_URL, url); |
| } |
| |
| /** |
| * If {@link #setDefaultTargetURL(String)}was not called, the source document ( |
| * {@link #getSourceURL()}) is returned. |
| * @see org.apache.lenya.cms.usecase.Usecase#getTargetURL(boolean) |
| */ |
| public String getTargetURL(boolean success) { |
| String url = getParameterAsString(EXIT_URI); |
| if (url == null) { |
| url = getParameterAsString(DEFAULT_TARGET_URL); |
| } |
| if (url == null) { |
| url = getSourceURL(); |
| } |
| return url + getExitQueryString(); |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#setPart(java.lang.String, |
| * org.apache.cocoon.servlet.multipart.Part) |
| */ |
| public void setPart(String name, Part value) { |
| if (!Part.class.isInstance(value)) { |
| String className = ""; |
| if (value != null) { |
| className = value.getClass().getName(); |
| } |
| throw new RuntimeException("[" + name + "] = (" + className + ") [" + value |
| + "] is not a part object. Maybe you have to enable uploads?"); |
| } |
| setParameter(name, value); |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#getPart(java.lang.String) |
| */ |
| public Part getPart(String name) { |
| return (Part) getParameter(name); |
| } |
| |
| protected DocumentFactory getDocumentFactory() { |
| DocumentFactory factory = (DocumentFactory) getParameter(PARAMETER_FACTORY); |
| Session session = getSession(); |
| if (factory == null || factory.getSession() != session) { |
| factory = DocumentUtil.createDocumentFactory(this.manager, session); |
| setParameter(PARAMETER_FACTORY, factory); |
| } |
| return factory; |
| } |
| |
| /** |
| * @see org.apache.avalon.framework.activity.Initializable#initialize() |
| */ |
| public final void initialize() throws Exception { |
| Request request = ContextHelper.getRequest(this.context); |
| Session session = RepositoryUtil.getSession(this.manager, request); |
| setSession(session); |
| setParameter(PARAMETER_STATE_MACHINE, new StateMachine(MODEL)); |
| } |
| |
| /** |
| * Does the actual initialization. Template method. |
| */ |
| protected final void doInitialize() { |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#advance() |
| */ |
| public void advance() throws UsecaseException { |
| // do nothing |
| } |
| |
| /** |
| * Deletes a parameter. |
| * @param name The parameter name. |
| */ |
| protected void deleteParameter(String name) { |
| this.parameters.remove(name); |
| } |
| |
| private String name; |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#setName(java.lang.String) |
| */ |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#getName() |
| */ |
| public String getName() { |
| return this.name; |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#getParameterNames() |
| */ |
| public String[] getParameterNames() { |
| initializeParametersIfNotDone(); |
| Set keys = this.parameters.keySet(); |
| return (String[]) keys.toArray(new String[keys.size()]); |
| } |
| |
| protected void initializeParametersIfNotDone() { |
| if (this.parameters.get(PARAMETERS_INITIALIZED) == null) { |
| this.parameters.put(PARAMETERS_INITIALIZED, Boolean.TRUE); |
| initParameters(); |
| } |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#setSourceURL(java.lang.String) |
| */ |
| public void setSourceURL(String url) { |
| setParameter(SOURCE_URL, url); |
| } |
| |
| private UsecaseView view; |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#getView() |
| */ |
| public UsecaseView getView() { |
| try { |
| prepareView(); |
| } catch (Exception e) { |
| getLogger().error("View preparation for usecase [" + getName() + "] failed: ", e); |
| addErrorMessage(e.getMessage()); |
| } |
| return this.view; |
| } |
| |
| /** |
| * Override this method to prepare the view (add information messages etc.). |
| * @throws Exception If an error occurs. |
| */ |
| protected void prepareView() throws Exception { |
| } |
| |
| protected static final String ELEMENT_PARAMETER = "parameter"; |
| protected static final String ATTRIBUTE_NAME = "name"; |
| protected static final String ATTRIBUTE_VALUE = "value"; |
| protected static final String ELEMENT_VIEW = "view"; |
| protected static final String ELEMENT_TRANSACTION = "transaction"; |
| protected static final String ATTRIBUTE_POLICY = "policy"; |
| protected static final String VALUE_OPTIMISTIC = "optimistic"; |
| protected static final String VALUE_PESSIMISTIC = "pessimistic"; |
| protected static final String ELEMENT_EXIT = "exit"; |
| protected static final String ATTRIBUTE_USECASE = "usecase"; |
| |
| private boolean isOptimistic = true; |
| |
| /** |
| * @return <code>true</code> if the transaction policy is optimistic offline lock, |
| * <code>false</code> if it is pessimistic offline lock. |
| */ |
| public boolean isOptimistic() { |
| return this.isOptimistic; |
| } |
| |
| /** |
| * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) |
| */ |
| public void configure(Configuration config) throws ConfigurationException { |
| |
| Configuration[] parameterConfigs = config.getChildren(ELEMENT_PARAMETER); |
| for (int i = 0; i < parameterConfigs.length; i++) { |
| String name = parameterConfigs[i].getAttribute(ATTRIBUTE_NAME); |
| String value = parameterConfigs[i].getAttribute(ATTRIBUTE_VALUE); |
| setParameter(name, value); |
| } |
| |
| Configuration viewConfig = config.getChild(ELEMENT_VIEW, false); |
| if (viewConfig != null) { |
| this.view = new UsecaseView(); |
| try { |
| view.service(this.manager); |
| } catch (ServiceException e) { |
| throw new ConfigurationException("Couldn't service view: ", e); |
| } |
| view.configure(viewConfig); |
| } |
| |
| Configuration transactionConfig = config.getChild(ELEMENT_TRANSACTION, false); |
| if (transactionConfig != null) { |
| String policy = transactionConfig.getAttribute(ATTRIBUTE_POLICY); |
| if (policy.equals(VALUE_PESSIMISTIC)) { |
| this.isOptimistic = false; |
| } |
| } |
| |
| Configuration exitConfig = config.getChild(ELEMENT_EXIT, false); |
| if (exitConfig != null) { |
| this.exitUsecaseName = exitConfig.getAttribute(ATTRIBUTE_USECASE); |
| Configuration[] exitParameterConfigs = exitConfig.getChildren(ELEMENT_PARAMETER); |
| for (int i = 0; i < exitParameterConfigs.length; i++) { |
| String name = exitParameterConfigs[i].getAttribute(ATTRIBUTE_NAME); |
| String value = null; |
| String[] attributeNames = exitParameterConfigs[i].getAttributeNames(); |
| for (int j = 0; j < attributeNames.length; j++) { |
| if (attributeNames[j].equals(ATTRIBUTE_VALUE)) |
| value = exitParameterConfigs[i].getAttribute(ATTRIBUTE_VALUE); |
| } |
| setExitParameter(name, value); |
| } |
| } |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#setView(org.apache.lenya.cms.usecase.UsecaseView) |
| */ |
| public void setView(UsecaseView view) { |
| this.view = view; |
| } |
| |
| /** |
| * @return The objects that could be changed during the usecase. |
| * @throws UsecaseException if an error occurs. |
| */ |
| protected Node[] getNodesToLock() throws UsecaseException { |
| return new Node[0]; |
| } |
| |
| /** |
| * <p> |
| * This method starts the transaction and locks all involved objects immediately. |
| * This way, all changes to the objects in the session occur after the locking, |
| * avoiding overriding changes of other sessions. |
| * </p> |
| * <p> |
| * This method is locked via the class lock to avoid inter-usecase synchronization issues. |
| * </p> |
| * @see org.apache.lenya.cms.usecase.Usecase#lockInvolvedObjects() |
| */ |
| public final void lockInvolvedObjects() throws UsecaseException { |
| try { |
| startTransaction(); |
| } catch (RepositoryException e) { |
| throw new UsecaseException(e); |
| } |
| synchronized (TransactionLock.LOCK) { |
| lockInvolvedObjects(getNodesToLock()); |
| } |
| advanceState("lockInvolvedObjects"); |
| } |
| |
| /** |
| * Start a transaction by using a new, modifiable session. |
| * @throws RepositoryException if an error occurs. |
| */ |
| protected void startTransaction() throws RepositoryException { |
| if (this.commitEnabled) { |
| setSession(RepositoryUtil.createSession(this.manager, getSession().getIdentity(), true)); |
| } |
| } |
| |
| /** |
| * <p> |
| * Lock the objects, for example when you need to change them (for example, delete). If you know |
| * when entering the usecase what these objects are, you do not need to call this, the framework |
| * will take of it if you implement getObjectsToLock(). If you do not know in advance what the |
| * objects are, you can call this method explicitly when appropriate. |
| * </p> |
| * |
| * @param objects the transactionable objects to lock |
| * @throws UsecaseException if an error occurs. |
| * @see #lockInvolvedObjects() |
| * @see #getNodesToLock() |
| */ |
| public final void lockInvolvedObjects(Node[] objects) throws UsecaseException { |
| try { |
| for (int i = 0; i < objects.length; i++) { |
| if (!objects[i].isLocked()) { |
| objects[i].lock(); |
| } |
| if (!isOptimistic() && !objects[i].isCheckedOutBySession(getSession())) { |
| objects[i].checkout(checkoutRestrictedToSession()); |
| } |
| } |
| } catch (RepositoryException e) { |
| throw new UsecaseException(e); |
| } |
| } |
| |
| protected boolean canCheckOut(Node[] objects) throws RepositoryException { |
| boolean canExecute = true; |
| |
| for (int i = 0; i < objects.length; i++) { |
| if (objects[i].isCheckedOut() && !objects[i].isCheckedOutBySession(getSession())) { |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug( |
| "AbstractUsecase::lockInvolvedObjects() can not execute, object [" |
| + objects[i] + "] is already checked out"); |
| } |
| canExecute = false; |
| } |
| } |
| return canExecute; |
| } |
| |
| /** |
| * @see org.apache.lenya.cms.usecase.Usecase#cancel() |
| */ |
| public void cancel() throws UsecaseException { |
| if (getSession().isModifiable()) { |
| try { |
| getSession().rollback(); |
| } catch (Exception e) { |
| throw new UsecaseException(e); |
| } |
| } |
| } |
| |
| private String exitUsecaseName = null; |
| private Map exitUsecaseParameters = new HashMap(); |
| |
| /** |
| * Sets a parameter to pass to the exit usecase. |
| * @param name The parameter name. |
| * @param value The parameter value. |
| */ |
| protected void setExitParameter(String name, String value) { |
| this.exitUsecaseParameters.put(name, value); |
| } |
| |
| /** |
| * Returns the query string to access the exit usecase of this usecase. |
| * @return A query string of the form |
| * <code>?lenya.usecase=...&param1=foo&param2=bar</code>. |
| */ |
| protected String getExitQueryString() { |
| StringBuffer queryBuffer = new StringBuffer(); |
| if (this.exitUsecaseName != null) { |
| queryBuffer.append("?lenya.usecase=").append(this.exitUsecaseName); |
| for (Iterator i = this.exitUsecaseParameters.keySet().iterator(); i.hasNext();) { |
| String key = (String) i.next(); |
| String value = (String) this.exitUsecaseParameters.get(key); |
| queryBuffer.append("&").append(key).append("=").append(value); |
| } |
| } else { |
| String exitUsecase = getParameterAsString("lenya.exitUsecase"); |
| if (exitUsecase != null && !"".equals(exitUsecase)) { |
| queryBuffer.append("?lenya.usecase=").append(exitUsecase); |
| } |
| } |
| return queryBuffer.toString(); |
| } |
| |
| public Session getSession() { |
| return (Session) getParameter(PARAMETER_SESSION); |
| } |
| |
| protected Context context; |
| |
| /** |
| * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) |
| */ |
| public void contextualize(Context context) throws ContextException { |
| this.context = context; |
| } |
| |
| protected ServiceManager manager; |
| |
| public void service(ServiceManager manager) throws ServiceException { |
| this.manager = manager; |
| } |
| |
| protected void setSession(org.apache.lenya.cms.repository.Session session) { |
| setParameter(PARAMETER_SESSION, session); |
| } |
| |
| private boolean commitEnabled = true; |
| |
| public void setTestSession(Session session) { |
| this.commitEnabled = false; |
| setSession(session); |
| } |
| |
| protected boolean checkoutRestrictedToSession() { |
| return getParameterAsBoolean(PARAMETER_CHECKOUT_RESTRICTED_TO_SESSION, true); |
| } |
| |
| } |