001// Copyright 2004, 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.hivemind.lib.pipeline;
016
017import java.util.List;
018
019import org.apache.hivemind.ErrorLog;
020import org.apache.hivemind.Location;
021import org.apache.hivemind.impl.BaseLocatable;
022import org.apache.hivemind.lib.DefaultImplementationBuilder;
023import org.apache.hivemind.order.Orderer;
024import org.apache.hivemind.service.ClassFactory;
025
026/**
027 * Used by the {@link org.apache.hivemind.lib.pipeline.PipelineFactory} to assemble the pipeline.
028 * 
029 * @author Howard Lewis Ship
030 */
031public class PipelineAssembler extends BaseLocatable
032{
033    /** @since 1.1 */
034    private ErrorLog _errorLog;
035
036    private String _serviceId;
037
038    private Class _serviceInterface;
039
040    private Class _filterInterface;
041
042    private ClassFactory _classFactory;
043
044    private DefaultImplementationBuilder _defaultBuilder;
045
046    private Orderer _orderer;
047
048    private Object _terminator;
049
050    private Location _terminatorLocation;
051
052    /**
053     * @param errorLog
054     *            used for reporting recoverable errors
055     * @param serviceInterface
056     *            the main interface
057     * @param filterInterface
058     *            the interface for filters
059     * @param classFactory
060     *            for creating new classes
061     * @param defaultBuilder
062     *            used to provide a placeholder terminator if no real terminator is supplied
063     * @param servceId
064     *            of the service being assembled
065     */
066    public PipelineAssembler(ErrorLog errorLog, String serviceId, Class serviceInterface,
067            Class filterInterface, ClassFactory classFactory,
068            DefaultImplementationBuilder defaultBuilder)
069    {
070        _errorLog = errorLog;
071        _serviceId = serviceId;
072        _serviceInterface = serviceInterface;
073        _filterInterface = filterInterface;
074        _classFactory = classFactory;
075        _defaultBuilder = defaultBuilder;
076
077        _orderer = new Orderer(_errorLog, "filter");
078
079    }
080
081    public void addFilter(String name, String prereqs, String postreqs, Object filter,
082            Location location)
083    {
084        if (!checkInterface(_filterInterface, filter, location))
085            return;
086
087        FilterHolder holder = new FilterHolder(filter, location);
088
089        _orderer.add(holder, name, prereqs, postreqs);
090    }
091
092    public void setTerminator(Object terminator, Location terminatorLocation)
093    {
094        if (_terminator != null)
095        {
096            _errorLog.error(PipelineMessages.duplicateTerminator(
097                    terminator,
098                    _serviceId,
099                    _terminator,
100                    _terminatorLocation), terminatorLocation, null);
101            return;
102        }
103
104        if (!checkInterface(_serviceInterface, terminator, terminatorLocation))
105            return;
106
107        _terminator = terminator;
108        _terminatorLocation = terminatorLocation;
109    }
110
111    // For testing
112
113    Object getTerminator()
114    {
115        return _terminator;
116    }
117
118    private boolean checkInterface(Class interfaceType, Object instance, Location location)
119    {
120        if (interfaceType.isAssignableFrom(instance.getClass()))
121            return true;
122
123        _errorLog.error(
124                PipelineMessages.incorrectInterface(instance, interfaceType, _serviceId),
125                location,
126                null);
127
128        return false;
129    }
130
131    /**
132     * Returns an object that implements the service interface, and integrates any filters for the
133     * pipeline with the
134     */
135    public Object createPipeline()
136    {
137        List filterHolders = _orderer.getOrderedObjects();
138        int count = filterHolders.size();
139
140        BridgeBuilder bb = (count == 0) ? null : new BridgeBuilder(_errorLog, _serviceId,
141                _serviceInterface, _filterInterface, _classFactory);
142
143        Object next = _terminator != null ? _terminator : _defaultBuilder
144                .buildDefaultImplementation(_serviceInterface);
145
146        // Like service interceptors, we work deepest (last) to shallowest (first).
147
148        for (int i = count - 1; i >= 0; i--)
149        {
150            FilterHolder h = (FilterHolder) filterHolders.get(i);
151            Object filter = h.getFilter();
152
153            next = bb.instantiateBridge(next, filter);
154        }
155
156        return next;
157    }
158}