001// Copyright 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry.record;
016
017import java.util.Collection;
018import java.util.Iterator;
019
020import org.apache.hivemind.ApplicationRuntimeException;
021import org.apache.hivemind.ErrorLog;
022import org.apache.hivemind.util.Defense;
023import org.apache.hivemind.util.PropertyUtils;
024import org.apache.tapestry.IComponent;
025import org.apache.tapestry.IPage;
026import org.apache.tapestry.IRequestCycle;
027import org.apache.tapestry.engine.IPageRecorder;
028import org.apache.tapestry.event.ObservedChangeEvent;
029import org.apache.tapestry.spec.IPropertySpecification;
030
031/**
032 * @author Howard M. Lewis Ship
033 * @since 4.0
034 */
035public class PageRecorderImpl implements IPageRecorder
036{
037    private String _pageName;
038
039    private IRequestCycle _requestCycle;
040
041    private PropertyPersistenceStrategySource _strategySource;
042
043    private boolean _locked = false;
044
045    private ErrorLog _log;
046
047    public PageRecorderImpl(String pageName, IRequestCycle requestCycle,
048            PropertyPersistenceStrategySource strategySource, ErrorLog log)
049    {
050        Defense.notNull(pageName, "pageName");
051        Defense.notNull(requestCycle, "requestCycle");
052        Defense.notNull(strategySource, "strategySource");
053        Defense.notNull(log, "log");
054
055        _pageName = pageName;
056        _requestCycle = requestCycle;
057        _strategySource = strategySource;
058        _log = log;
059    }
060
061    public void commit()
062    {
063        _locked = true;
064    }
065
066    public Collection getChanges()
067    {
068        return _strategySource.getAllStoredChanges(_pageName);
069    }
070
071    public void rollback(IPage page)
072    {
073        Collection changes = getChanges();
074
075        Iterator i = changes.iterator();
076
077        while (i.hasNext())
078        {
079            PropertyChange change = (PropertyChange) i.next();
080
081            applyChange(page, change);
082        }
083    }
084
085    private void applyChange(IPage page, PropertyChange change)
086    {
087        String idPath = change.getComponentPath();
088
089        IComponent component = (idPath == null) ? page : page.getNestedComponent(idPath);
090
091        PropertyUtils.write(component, change.getPropertyName(), change.getNewValue());
092    }
093
094    public void observeChange(ObservedChangeEvent event)
095    {
096        IComponent component = event.getComponent();
097        String propertyName = event.getPropertyName();
098
099        if (_locked)
100        {
101            _log.error(RecordMessages.recorderLocked(propertyName, component), null, null);
102            return;
103        }
104
105        PropertyPersistenceStrategy strategy = findStrategy(component, propertyName);
106
107        if (strategy != null)
108            strategy.store(_pageName, component.getIdPath(), propertyName, event.getNewValue());
109    }
110
111    // package private for testing
112
113    PropertyPersistenceStrategy findStrategy(IComponent component, String propertyName)
114    {
115        // So much for Law of Demeter!
116
117        IPropertySpecification propertySpecification = component.getSpecification()
118                .getPropertySpecification(propertyName);
119
120        if (propertySpecification == null)
121        {
122            _log.error(
123                    RecordMessages.missingPropertySpecification(propertyName, component),
124                    null,
125                    null);
126            return null;
127        }
128
129        String name = propertySpecification.getPersistence();
130
131        // Should check for nulls, but the architecture of the framework pretty much
132        // ensures that we won't get here unless there is a property
133        // and a persistence value for the property.
134
135        try
136        {
137            return _strategySource.getStrategy(name);
138        }
139        catch (ApplicationRuntimeException ex)
140        {
141            _log.error(ex.getMessage(), propertySpecification.getLocation(), ex);
142
143            return null;
144        }
145    }
146
147}