public JoinSetComposerDesc Create(Viewable[] streamViews, bool isFireAndForget, AgentInstanceContext agentInstanceContext, bool isRecoveringResilient)
        {
            // Build indexes
            var indexesPerStream         = new IDictionary <TableLookupIndexReqKey, EventTable> [_indexSpecs.Length];
            var tableSecondaryIndexLocks = new ILockable[_indexSpecs.Length];
            var hasTable = false;

            for (var streamNo = 0; streamNo < _indexSpecs.Length; streamNo++)
            {
                if (_indexSpecs[streamNo] == null)
                {
                    continue;
                }

                var items = _indexSpecs[streamNo].Items;
                indexesPerStream[streamNo] = new LinkedHashMap <TableLookupIndexReqKey, EventTable>();

                if (_streamJoinAnalysisResult.TablesPerStream[streamNo] != null)
                {
                    // build for tables
                    var metadata = _streamJoinAnalysisResult.TablesPerStream[streamNo];
                    var state    = _tableService.GetState(metadata.TableName, agentInstanceContext.AgentInstanceId);
                    foreach (var indexName in state.SecondaryIndexes)       // add secondary indexes
                    {
                        indexesPerStream[streamNo].Put(new TableLookupIndexReqKey(indexName, metadata.TableName), state.GetIndex(indexName));
                    }
                    var index = state.GetIndex(metadata.TableName);     // add primary index
                    indexesPerStream[streamNo].Put(new TableLookupIndexReqKey(metadata.TableName, metadata.TableName), index);
                    hasTable = true;
                    tableSecondaryIndexLocks[streamNo] = agentInstanceContext.StatementContext.IsWritesToTables ?
                                                         state.TableLevelRWLock.WriteLock : state.TableLevelRWLock.ReadLock;
                }
                else
                {
                    // build tables for implicit indexes
                    foreach (var entry in items)
                    {
                        EventTable index;
                        if (_streamJoinAnalysisResult.ViewExternal[streamNo] != null)
                        {
                            VirtualDWView view = _streamJoinAnalysisResult.ViewExternal[streamNo].Invoke(agentInstanceContext);
                            index = view.GetJoinIndexTable(items.Get(entry.Key));
                        }
                        else
                        {
                            index = EventTableUtil.BuildIndex(
                                agentInstanceContext, streamNo, items.Get(entry.Key), _streamTypes[streamNo], false,
                                entry.Value.IsUnique, null, null, isFireAndForget);
                        }
                        indexesPerStream[streamNo].Put(entry.Key, index);
                    }
                }
            }

            // obtain any external views
            var externalViewProviders = _streamJoinAnalysisResult.ViewExternal;
            var externalViews         = new VirtualDWView[externalViewProviders.Length];

            for (var i = 0; i < externalViews.Length; i++)
            {
                if (externalViewProviders[i] != null)
                {
                    externalViews[i] = _streamJoinAnalysisResult.ViewExternal[i].Invoke(agentInstanceContext);
                }
            }

            // Build strategies
            var queryExecSpecs  = _queryPlan.ExecNodeSpecs;
            var queryStrategies = new QueryStrategy[queryExecSpecs.Length];

            for (var i = 0; i < queryExecSpecs.Length; i++)
            {
                var planNode = queryExecSpecs[i];
                if (planNode == null)
                {
                    Log.Debug(".MakeComposer No execution node for stream " + i + " '" + _streamNames[i] + "'");
                    continue;
                }

                var executionNode = planNode.MakeExec(
                    _statementName, _statementId, _annotations, indexesPerStream, _streamTypes, streamViews,
                    _historicalStreamIndexLists, externalViews, tableSecondaryIndexLocks);

                if (Log.IsDebugEnabled)
                {
                    Log.Debug(".MakeComposer Execution nodes for stream " + i + " '" + _streamNames[i] +
                              "' : \n" + ExecNode.Print(executionNode));
                }

                queryStrategies[i] = new ExecNodeQueryStrategy(i, _streamTypes.Length, executionNode);
            }

            // Remove indexes that are from tables as these are only available to query strategies
            if (hasTable)
            {
                indexesPerStream = RemoveTableIndexes(indexesPerStream, _streamJoinAnalysisResult.TablesPerStream);
            }

            // If this is not unidirectional and not a self-join (excluding self-outer-join)
            JoinSetComposerDesc joinSetComposerDesc;

            if ((!_streamJoinAnalysisResult.IsUnidirectional) &&
                (!_streamJoinAnalysisResult.IsPureSelfJoin || _outerJoinDescList.Length > 0))
            {
                JoinSetComposer composer;
                if (_historicalViewableDesc.HasHistorical)
                {
                    composer = new JoinSetComposerHistoricalImpl(_eventTableIndexService.AllowInitIndex(isRecoveringResilient), indexesPerStream, queryStrategies, streamViews, _exprEvaluatorContext);
                }
                else
                {
                    if (isFireAndForget)
                    {
                        composer = new JoinSetComposerFAFImpl(indexesPerStream, queryStrategies, _streamJoinAnalysisResult.IsPureSelfJoin, _exprEvaluatorContext, _joinRemoveStream, _isOuterJoins);
                    }
                    else
                    {
                        composer = new JoinSetComposerImpl(_eventTableIndexService.AllowInitIndex(isRecoveringResilient), indexesPerStream, queryStrategies, _streamJoinAnalysisResult.IsPureSelfJoin, _exprEvaluatorContext, _joinRemoveStream);
                    }
                }

                // rewrite the filter expression for all-inner joins in case "on"-clause outer join syntax was used to include those expressions
                var filterExpression = GetFilterExpressionInclOnClause(_optionalFilterNode, _outerJoinDescList);

                var postJoinEval = filterExpression == null ? null : filterExpression.ExprEvaluator;
                joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval);
            }
            else
            {
                QueryStrategy driver;
                int           unidirectionalStream;
                if (_streamJoinAnalysisResult.UnidirectionalStreamNumber != -1)
                {
                    unidirectionalStream = _streamJoinAnalysisResult.UnidirectionalStreamNumber;
                    driver = queryStrategies[unidirectionalStream];
                }
                else
                {
                    unidirectionalStream = 0;
                    driver = queryStrategies[0];
                }

                var composer = new JoinSetComposerStreamToWinImpl(_eventTableIndexService.AllowInitIndex(isRecoveringResilient), indexesPerStream, _streamJoinAnalysisResult.IsPureSelfJoin,
                                                                  unidirectionalStream, driver, _streamJoinAnalysisResult.UnidirectionalNonDriving);
                var postJoinEval = _optionalFilterNode == null ? null : _optionalFilterNode.ExprEvaluator;
                joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval);
            }

            // init if the join-set-composer allows it
            if (joinSetComposerDesc.JoinSetComposer.AllowsInit)
            {
                // compile prior events per stream to preload any indexes
                var eventsPerStream = new EventBean[_streamNames.Length][];
                var events          = new List <EventBean>();
                for (var i = 0; i < eventsPerStream.Length; i++)
                {
                    // For named windows and tables, we don't need to preload indexes from the iterators as this is always done already
                    if (_streamJoinAnalysisResult.NamedWindow[i] || _streamJoinAnalysisResult.TablesPerStream[i] != null)
                    {
                        continue;
                    }

                    IEnumerator <EventBean> it = null;
                    if (!(streamViews[i] is HistoricalEventViewable) && !(streamViews[i] is DerivedValueView))
                    {
                        try {
                            it = streamViews[i].GetEnumerator();
                        } catch (UnsupportedOperationException ex) {
                            // Joins do not support the iterator
                        }
                    }

                    if (it != null)
                    {
                        while (it.MoveNext())
                        {
                            events.Add(it.Current);
                        }
                        eventsPerStream[i] = events.ToArray();
                        events.Clear();
                    }
                    else
                    {
                        eventsPerStream[i] = new EventBean[0];
                    }
                }

                // init
                joinSetComposerDesc.JoinSetComposer.Init(eventsPerStream);
            }

            return(joinSetComposerDesc);
        }