| // Copyright 2004 The Apache Software Foundation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package org.apache.tapestry.engine; |
| |
| import org.apache.commons.lang.builder.ToStringBuilder; |
| import org.apache.tapestry.ApplicationRuntimeException; |
| import org.apache.tapestry.IRequestCycle; |
| import org.apache.tapestry.Tapestry; |
| import org.apache.tapestry.record.SessionPageRecorder; |
| |
| import java.io.IOException; |
| import java.io.ObjectInput; |
| import java.io.ObjectOutput; |
| import java.util.*; |
| |
| /** |
| * Concrete implementation of {@link org.apache.tapestry.IEngine} used for ordinary |
| * applications. All page state information is maintained in |
| * the {@link javax.servlet.http.HttpSession} using |
| * instances of {@link org.apache.tapestry.record.SessionPageRecorder}. |
| * |
| * @author Howard Lewis Ship |
| * @version $Id$ |
| * |
| **/ |
| |
| public class BaseEngine extends AbstractEngine |
| { |
| private static final long serialVersionUID = -7051050643746333380L; |
| |
| private final static int MAP_SIZE = 3; |
| |
| private transient Map _recorders; |
| |
| private transient Set _activePageNames; |
| |
| /** |
| * Removes all page recorders that contain no changes, or |
| * are marked for discard. Subclasses |
| * should invoke this implementation in addition to providing |
| * thier own. |
| * |
| **/ |
| |
| protected void cleanupAfterRequest(IRequestCycle cycle) |
| { |
| if (Tapestry.isEmpty(_recorders)) |
| return; |
| |
| boolean markDirty = false; |
| Iterator i = _recorders.entrySet().iterator(); |
| |
| while (i.hasNext()) |
| { |
| Map.Entry entry = (Map.Entry) i.next(); |
| String pageName = (String) entry.getKey(); |
| IPageRecorder recorder = (IPageRecorder) entry.getValue(); |
| |
| if (!recorder.getHasChanges() || recorder.isMarkedForDiscard()) |
| { |
| recorder.discard(); |
| |
| i.remove(); |
| |
| _activePageNames.remove(pageName); |
| |
| markDirty = true; |
| } |
| } |
| |
| if (markDirty) |
| markDirty(); |
| } |
| |
| public void forgetPage(String name) |
| { |
| if (_recorders == null) |
| return; |
| |
| IPageRecorder recorder = (IPageRecorder) _recorders.get(name); |
| if (recorder == null) |
| return; |
| |
| if (recorder.isDirty()) |
| throw new ApplicationRuntimeException( |
| Tapestry.format("BaseEngine.recorder-has-uncommited-changes", name)); |
| |
| recorder.discard(); |
| _recorders.remove(name); |
| _activePageNames.remove(name); |
| |
| markDirty(); |
| } |
| |
| /** |
| * Returns an unmodifiable {@link Collection} of the page names for which |
| * {@link IPageRecorder} instances exist. |
| * |
| * |
| **/ |
| |
| public Collection getActivePageNames() |
| { |
| if (_activePageNames == null) |
| return Collections.EMPTY_LIST; |
| |
| return Collections.unmodifiableCollection(_activePageNames); |
| } |
| |
| public IPageRecorder getPageRecorder(String pageName, IRequestCycle cycle) |
| { |
| if (_activePageNames == null || !_activePageNames.contains(pageName)) |
| return null; |
| |
| IPageRecorder result = null; |
| |
| if (_recorders != null) |
| return result = (IPageRecorder) _recorders.get(pageName); |
| |
| // So the page is active, but not in the cache of page recoders, |
| // so (re-)create the page recorder. |
| |
| if (result == null) |
| result = createPageRecorder(pageName, cycle); |
| |
| return result; |
| } |
| |
| public IPageRecorder createPageRecorder(String pageName, IRequestCycle cycle) |
| { |
| if (_recorders == null) |
| _recorders = new HashMap(); |
| else |
| { |
| if (_recorders.containsKey(pageName)) |
| throw new ApplicationRuntimeException( |
| Tapestry.format("BaseEngine.duplicate-page-recorder", pageName)); |
| } |
| |
| // Force the creation of the HttpSession |
| |
| cycle.getRequestContext().createSession(); |
| setStateful(); |
| |
| IPageRecorder result = new SessionPageRecorder(); |
| result.initialize(pageName, cycle); |
| |
| _recorders.put(pageName, result); |
| |
| if (_activePageNames == null) |
| _activePageNames = new HashSet(); |
| |
| _activePageNames.add(pageName); |
| |
| markDirty(); |
| |
| return result; |
| } |
| |
| /** |
| * Reconstructs the list of active page names |
| * written by {@link #writeExternal(ObjectOutput)}. |
| * |
| **/ |
| |
| public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException |
| { |
| super.readExternal(in); |
| |
| int count = in.readInt(); |
| |
| if (count > 0) |
| _activePageNames = new HashSet(count); |
| |
| for (int i = 0; i < count; i++) |
| { |
| String name = in.readUTF(); |
| |
| _activePageNames.add(name); |
| } |
| |
| } |
| |
| /** |
| * Writes the engine's persistent state; this is simply the list of active page |
| * names. For efficiency, this is written as a count followed by each name |
| * as a UTF String. |
| * |
| **/ |
| |
| public void writeExternal(ObjectOutput out) throws IOException |
| { |
| super.writeExternal(out); |
| |
| if (Tapestry.isEmpty(_activePageNames)) |
| { |
| out.writeInt(0); |
| return; |
| } |
| |
| int count = _activePageNames.size(); |
| |
| out.writeInt(count); |
| |
| Iterator i = _activePageNames.iterator(); |
| |
| while (i.hasNext()) |
| { |
| String name = (String) i.next(); |
| |
| out.writeUTF(name); |
| } |
| } |
| |
| public void extendDescription(ToStringBuilder builder) |
| { |
| builder.append("activePageNames", _activePageNames); |
| } |
| |
| } |