Package com.hp.hpl.jena.sparql.engine

Source Code of com.hp.hpl.jena.sparql.engine.QueryExecutionBase$TimeoutCallback

/*
* 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 com.hp.hpl.jena.sparql.engine;

import java.util.HashSet ;
import java.util.Iterator ;
import java.util.List ;
import java.util.Set ;
import java.util.concurrent.TimeUnit ;

import org.apache.jena.atlas.lib.AlarmClock ;
import org.apache.jena.atlas.logging.Log ;

import com.hp.hpl.jena.graph.Node ;
import com.hp.hpl.jena.graph.Triple ;
import com.hp.hpl.jena.n3.IRIResolver ;
import com.hp.hpl.jena.query.* ;
import com.hp.hpl.jena.rdf.model.* ;
import com.hp.hpl.jena.shared.PrefixMapping ;
import com.hp.hpl.jena.sparql.ARQConstants ;
import com.hp.hpl.jena.sparql.core.DatasetGraph ;
import com.hp.hpl.jena.sparql.core.describe.DescribeHandler ;
import com.hp.hpl.jena.sparql.core.describe.DescribeHandlerRegistry ;
import com.hp.hpl.jena.sparql.engine.binding.Binding ;
import com.hp.hpl.jena.sparql.engine.binding.BindingRoot ;
import com.hp.hpl.jena.sparql.engine.binding.BindingUtils ;
import com.hp.hpl.jena.sparql.engine.iterator.QueryIteratorWrapper ;
import com.hp.hpl.jena.sparql.graph.GraphFactory ;
import com.hp.hpl.jena.sparql.modify.TemplateLib ;
import com.hp.hpl.jena.sparql.syntax.ElementGroup ;
import com.hp.hpl.jena.sparql.syntax.Template ;
import com.hp.hpl.jena.sparql.util.Context ;
import com.hp.hpl.jena.sparql.util.DatasetUtils ;
import com.hp.hpl.jena.sparql.util.ModelUtils ;
import com.hp.hpl.jena.sparql.util.NodeFactoryExtra ;
import com.hp.hpl.jena.util.FileManager ;

/** All the SPARQL query result forms made from a graph-level execution object */

public class QueryExecutionBase implements QueryExecution
{
    // Pull over the "build dataset code"
    // Initial bindings.
    // Split : QueryExecutionGraph already has the dataset.

    private Query               query ;
    private Dataset             dataset ;
    private QueryEngineFactory  qeFactory ;
    private QueryIterator       queryIterator = null ;
    private Plan                plan = null ;
    private Context             context ;
    private FileManager         fileManager = FileManager.get() ;
    private QuerySolution       initialBinding = null ;
   
    // Set if QueryIterator.cancel has been called
    private volatile boolean    isCancelled = false ;
    private volatile TimeoutCallback expectedCallback = null ;   
    private TimeoutCallback timeout1Callback = null ;
    private TimeoutCallback timeout2Callback = null ;
   
    private final Object        lockTimeout = new Object() ;     // synchronization. 
    private static final long   TIMEOUT_UNSET = -1 ;
    private static final long   TIMEOUT_INF = -2 ;
    private long                timeout1 = TIMEOUT_UNSET ;
    private long                timeout2 = TIMEOUT_UNSET ;
    private final AlarmClock    alarmClock = AlarmClock.get()

    public QueryExecutionBase(Query query,
                              Dataset dataset,
                              Context context,
                              QueryEngineFactory qeFactory)
    {
        this.query = query ;
        this.dataset = dataset ;
        this.context = context ;
        this.qeFactory = qeFactory ;
        init() ;
    }
   
    private void init()
    {
        DatasetGraph dsg = (dataset == null) ? null : dataset.asDatasetGraph() ;
        context = Context.setupContext(context, dsg) ;
        if ( query != null )
            context.put(ARQConstants.sysCurrentQuery, query) ;
        // NB: Settign timeouts via the context after creating a QueryExecutionBase
        // will not work.
        // But we can't move it until the point the execution starts because of
        // get and set timeout oeprations on this object.  
        setAnyTimeouts() ;
    }
   
    // Put any per-dataset execution global configuration state here.
    public static Context setupContext(Context context, DatasetGraph dataset)
    {
        if ( context == null )
            context = ARQ.getContext() ;    // Already copied?
        context = context.copy() ;

        if ( dataset != null && dataset.getContext() != null )
            // Copy per-dataset settings.
            context.putAll(dataset.getContext()) ;
       
        context.set(ARQConstants.sysCurrentTime, NodeFactoryExtra.nowAsDateTime()) ;
       
        // Allocators.
//        context.set(ARQConstants.sysVarAllocNamed, new VarAlloc(ARQConstants.allocVarMarkerExec)) ;
//        context.set(ARQConstants.sysVarAllocAnon,  new VarAlloc(ARQConstants.allocVarAnonMarkerExec)) ;
        // Add VarAlloc for variables and bNodes (this is not the parse name).
        // More added later e.g. query (if there is a query), algebra form (in setOp)
       
        return context ;
    }
   
    private void setAnyTimeouts()
    {
        if ( context.isDefined(ARQ.queryTimeout) )
        {
            Object obj = context.get(ARQ.queryTimeout) ;
            if ( obj instanceof Number )
            {
                long x = ((Number)obj).longValue() ;
                setTimeout(x) ;
            } else if ( obj instanceof String )
            {
                try {
                    String str = obj.toString() ;
                    if ( str.contains(",") )
                    {
                        String[] a = str.split(",") ;
                        long x1 = Long.parseLong(a[0]) ;
                        long x2 = Long.parseLong(a[1]) ;
                        setTimeout(x1, x2) ;
                    }
                    else
                    {
                        long x = Long.parseLong(str) ;
                        setTimeout(x) ;
                    }
                } catch (RuntimeException ex) { Log.warn(this, "Can't interpret string for timeout: "+obj) ; }
            }
            else
                Log.warn(this, "Can't interpret timeout: "+obj) ;
        }
    }
   
    @Override
    public void close()
    {
        if ( queryIterator != null )
            queryIterator.close() ;
        if ( plan != null )
            plan.close() ;
        if ( timeout1Callback != null )
            alarmClock.cancel(timeout1Callback) ;
        if ( timeout2Callback != null )
            alarmClock.cancel(timeout2Callback) ;
    }

    @Override
    public void abort()
    {
        synchronized(lockTimeout)
        {
            // This is called asynchronously to the execution.
            // synchronized is for coordination with other calls of
            // .abort and with the timeout2 reset code.
            if ( queryIterator != null )
                // we notify the chain of iterators, however, we do *not* close the iterators.
                // That happens after the cancellation is properly over.
                queryIterator.cancel() ;
            isCancelled = true ;
        }
    }
   
    @Override
    public ResultSet execSelect()
    {
        if ( ! query.isSelectType() )
            throw new QueryExecException("Attempt to have ResultSet from a "+labelForQuery(query)+" query") ;
        return execResultSet() ;
    }

    // Construct
    @Override
    public Model execConstruct()
    {
        return execConstruct(GraphFactory.makeJenaDefaultModel()) ;
    }

//    /**
//     * Executes as a construct query, placing the results into a newly constructed {@link com.hp.hpl.jena.sparql.graph.GraphDistinctDataBag}.
//     * The threshold policy is set from the current context.
//     */
//    @Override
//    public Model execConstructDataBag()
//    {
//        ThresholdPolicy<Triple> thresholdPolicy = ThresholdPolicyFactory.policyFromContext(context);
//        return execConstruct(GraphFactory.makeDataBagModel(thresholdPolicy)) ;
//    }
   
    @Override
    public Model execConstruct(Model model)
    {
        try
        {
            Iterator<Triple> it = execConstructTriples();
           
            // Prefixes for result
            insertPrefixesInto(model);
           
            while (it.hasNext())
            {
                Triple t = it.next();
                Statement stmt = ModelUtils.tripleToStatement(model, t);
                if ( stmt != null )
                    model.add(stmt);
            }
        }
        finally
        {
            this.close();
        }
        return model;
    }
   
    @Override
    public Iterator<Triple> execConstructTriples()
    {
        if ( ! query.isConstructType() )
            throw new QueryExecException("Attempt to get a CONSTRUCT model from a "+labelForQuery(query)+" query") ;
        // This causes there to be no PROJECT around the pattern.
        // That in turn, exposes the initial bindings. 
        query.setQueryResultStar(true) ;

        startQueryIterator() ;
       
        Template template = query.getConstructTemplate() ;
        return TemplateLib.calcTriples(template.getTriples(), queryIterator);
    }

    @Override
    public Model execDescribe()
    { return execDescribe(GraphFactory.makeJenaDefaultModel()) ; }


    @Override
    public Model execDescribe(Model model)
    {
        if ( ! query.isDescribeType() )
            throw new QueryExecException("Attempt to get a DESCRIBE result from a "+labelForQuery(query)+" query") ;
        //Was: query.setQueryResultStar(true) ; but why?
        query.setResultVars() ;
        // If there was no WhereClause, use an empty pattern (one solution, no columns).
        if ( query.getQueryPattern() == null )
            query.setQueryPattern(new ElementGroup()) ;
       
        Set<RDFNode> set = new HashSet<RDFNode>() ;

        //May return null (no query pattern)
        ResultSet qRes = execResultSet() ;

        // Prefixes for result (after initialization)
        insertPrefixesInto(model) ;
        if ( qRes != null )
        {
            for ( ; qRes.hasNext() ; )
            {
                QuerySolution rb = qRes.nextSolution() ;
                for ( String varName : query.getResultVars() )
                {
                    RDFNode n = rb.get(varName) ;
                    set.add(n) ;
                }
            }
        }

        if ( query.getResultURIs() != null )
        {
            // Any URIs in the DESCRIBE
            for (Node n : query.getResultURIs())
            {
                // Need to make dataset available to describe handlers.
                RDFNode rNode = ModelUtils.convertGraphNodeToRDFNode(n, dataset.getDefaultModel()) ;
                set.add(rNode) ;
            }
        }

        // Create new handlers for this process.
        List<DescribeHandler> dhList = DescribeHandlerRegistry.get().newHandlerList() ;

        getContext().put(ARQConstants.sysCurrentDataset, getDataset()) ;
        // Notify start of describe phase
        for (DescribeHandler dh : dhList)
            dh.start(model, getContext()) ;

        // Do describe for each resource found.
        for (Iterator<RDFNode> iter = set.iterator() ; iter.hasNext() ;)
        {
            RDFNode n = iter.next() ;

            if ( n instanceof Resource )
            {
                for (DescribeHandler dh : dhList)
                    dh.describe((Resource)n) ;
            }
            else
                // Can't describe literals
                continue ;
        }

        for (DescribeHandler dh : dhList)
            dh.finish() ;

        this.close() ;
        return model ;
    }
   
    // TODO not memory efficient
    @Override
    public Iterator<Triple> execDescribeTriples()
    {
        return ModelUtils.statementsToTriples(execDescribe().listStatements());
    }

    @Override
    public boolean execAsk()
    {
        if ( ! query.isAskType() )
            throw new QueryExecException("Attempt to have boolean from a "+labelForQuery(query)+" query") ;

        startQueryIterator() ;
        boolean r = queryIterator.hasNext() ;
        this.close() ;
        return r ;
    }

    @Override
    public void setTimeout(long timeout, TimeUnit timeUnit)
    {
        // Overall timeout - recorded as (UNSET,N)
        long x = asMillis(timeout, timeUnit) ;
        this.timeout1 = TIMEOUT_UNSET ;
        this.timeout2 = x ;
    }

    @Override
    public void setTimeout(long timeout)
    {
        setTimeout(timeout, TimeUnit.MILLISECONDS) ;
    }

    @Override
    public void setTimeout(long timeout1, TimeUnit timeUnit1, long timeout2, TimeUnit timeUnit2)
    {
        // Two timeouts.
        long x1 = asMillis(timeout1, timeUnit1) ;
        long x2 = asMillis(timeout2, timeUnit2) ;
        this.timeout1 = x1 ;
        if ( timeout2 < 0 )
            this.timeout2 = TIMEOUT_UNSET ;
        else
            this.timeout2 = x2 ;
    }

    @Override
    public void setTimeout(long timeout1, long timeout2)
    {
        setTimeout(timeout1, TimeUnit.MILLISECONDS, timeout2, TimeUnit.MILLISECONDS) ;
    }

    private static long asMillis(long duration, TimeUnit timeUnit)
    {
        return (duration < 0 ) ? duration : timeUnit.toMillis(duration) ;
    }
   
    @Override
    public long getTimeout1() { return timeout1 ; }
    @Override
    public long getTimeout2() { return timeout2 ; }
   
    private static boolean isTimeoutSet(long x)
    {
        return x >= 0 ;
    }

    class TimeoutCallback implements Runnable
    {
        @Override
        public void run()
        {
            synchronized(lockTimeout)
            {
                // Abort query if and only if we are the expected callback.
                // If the first row has appeared, and we are removing timeout1 callback,
                // it still may go off so it needs to check here it's still wanted.
                if ( expectedCallback == this )
                    QueryExecutionBase.this.abort() ;
            }
        }
    }
   
    private class QueryIteratorTimer2 extends QueryIteratorWrapper
    {
        public QueryIteratorTimer2(QueryIterator qIter)
        {
            super(qIter) ;
        }
       
        long yieldCount = 0 ;
        boolean resetDone = false ;
        @Override
        protected Binding moveToNextBinding()
        {
            Binding b = super.moveToNextBinding() ;
            yieldCount++ ;
           
            if ( ! resetDone )
            {
                // Sync on calls of .abort.
                // So nearly not needed.
                synchronized(lockTimeout)
                {
                    expectedCallback = timeout2Callback ;
                    // Lock against calls of .abort() nor of timeout1Callback.
                   
                    // Update/check the volatiles in a careful order.
                    // This cause timeout1 not to call .abort and hence not set isCancelled

                    // But if timeout1 went off after moveToNextBinding, before expectedCallback is set,
                    // then formget the row and cacnel the query.
                    if ( isCancelled )
                        // timeout1 went off after the binding was yielded but
                        // before we got here.
                        throw new QueryCancelledException() ;
                    if ( timeout1Callback != null )
                        alarmClock.cancel(timeout1Callback) ;
                        timeout1Callback = null ;

                    // Now arm the second timeout, if any.
                    if ( timeout2 > 0 )
                        // Not first timeout - finite second timeout.
                        alarmClock.add(timeout2Callback, timeout2) ;
                    resetDone = true ;
                }
            }
            return b ;
        }
    }
   
    protected void execInit() { }

    private ResultSet asResultSet(QueryIterator qIter)
    {
        Model model = null ;
        if ( dataset != null )
            model = dataset.getDefaultModel() ;
        else
            model = ModelFactory.createDefaultModel() ;
       
        ResultSetStream rStream = new ResultSetStream(query.getResultVars(), model, qIter) ;
        return rStream ;
    }
   
    /** Start the query iterator, setting timeouts as needed. */
    private void startQueryIterator()
    {
        execInit() ;
        if ( queryIterator != null )
            Log.warn(this, "Query iterator has already been started") ;
       
        /* Timeouts:
         * -1,-1                No timeouts
         * N, same as -1,N      Overall timeout only.  No wrapper needed.
         * N,-1                 Timeout on first row only. Need to cancel on first row.
         * N,M                  First/overall timeout. Need to reset on first row.
         */
       
        if ( ! isTimeoutSet(timeout1) && ! isTimeoutSet(timeout2) )
        {
            // Case -1,-1
            queryIterator = getPlan().iterator() ;
            return ;
        }
       
        if ( ! isTimeoutSet(timeout1) && isTimeoutSet(timeout2) )
        {
            // Single overall timeout.
            timeout2Callback = new TimeoutCallback() ;
            expectedCallback = timeout2Callback ;
            alarmClock.add(timeout2Callback, timeout2) ;
            // Start the query.
            queryIterator = getPlan().iterator() ;
            // But don't add resetter.
            return ;
        }

        // Case isTimeoutSet(timeout1)
        // Add timeout to first row.
        timeout1Callback = new TimeoutCallback() ;
        alarmClock.add(timeout1Callback, timeout1) ;
        expectedCallback = timeout1Callback ;

        // We don't know if getPlan().iterator() does a lot of work or not
        // (ideally it shouldn't start executing the query but in some sub-systems
        // it might be necessary)
        queryIterator = getPlan().iterator() ;
       
        // Add the timeout resetter wrapper.
        timeout2Callback = new TimeoutCallback() ;
        // Wrap with a resetter.
        queryIterator = new QueryIteratorTimer2(queryIterator) ;

        // Minor optimization - the first call of hasNext() or next() will
        // throw QueryCancelledExcetion anyway.  This just makes it a bit earlier
        // in the case when the timeout (timoeut1) is so short it's gone off already.
       
        if ( isCancelled ) queryIterator.cancel() ;
    }
   
    private ResultSet execResultSet()
    {
        startQueryIterator() ;
        return asResultSet(queryIterator) ;
    }

    public Plan getPlan()
    {
        if ( plan == null )
        {
            DatasetGraph dsg = prepareDataset(dataset, query, fileManager) ;
            Binding inputBinding = null ;
            if ( initialBinding != null )
                inputBinding = BindingUtils.asBinding(initialBinding) ;
            if ( inputBinding == null )
                inputBinding = BindingRoot.create() ;

            plan = qeFactory.create(query, dsg, inputBinding, getContext()) ;
        }           
        return plan ;
    }

    private void insertPrefixesInto(Model model)
    {
        try {
            if ( dataset != null )
            {
                // Load the models prefixes first
                PrefixMapping m = dataset.getDefaultModel() ;
                model.setNsPrefixes(m) ;
            }
            // Then add the queries (just the declared mappings)
            // so the query declarations override the data sources.
            model.setNsPrefixes(query.getPrefixMapping()) ;

        } catch (Exception ex)
        {
            Log.warn(this, "Exception in insertPrefixes: "+ex.getMessage(), ex) ;
        }
    }

    static private String labelForQuery(Query q)
    {
        if ( q.isSelectType() )     return "SELECT" ;
        if ( q.isConstructType() )  return "CONSTRUCT" ;
        if ( q.isDescribeType() )   return "DESCRIBE" ;
        if ( q.isAskType() )        return "ASK" ;
        return "<<unknown>>" ;
    }

    @Override
    public Context getContext() { return context ; }
   
    @Override
    public Dataset getDataset() { return dataset ; }

    @Override
    public Query getQuery()     { return query ; }

    // Call after setFM called.
    private static DatasetGraph prepareDataset(Dataset dataset, Query query, FileManager fileManager)
    {
        if ( dataset != null )
            return dataset.asDatasetGraph() ;
       
        if ( ! query.hasDatasetDescription() )
            //Query.Log.warn(this, "No data for query (no URL, no model)");
            throw new QueryExecException("No dataset description for query");
       
        String baseURI = query.getBaseURI() ;
        if ( baseURI == null )
            baseURI = IRIResolver.chooseBaseURI() ;
       
        DatasetGraph dsg = DatasetUtils.createDatasetGraph(query.getDatasetDescription(),
                                                           fileManager, baseURI ) ;
        return dsg ;
    }
   
    @Override
    public void setFileManager(FileManager fm) { fileManager = fm ; }
   
    @Override
    public void setInitialBinding(QuerySolution startSolution)
    {
        initialBinding = startSolution ;
    }
   
    //protected QuerySolution getInputBindings() { return initialBinding ; }

    public void setInitialBindings(ResultSet table)
    { throw new UnsupportedOperationException("setInitialBindings(ResultSet)") ; }
}
TOP

Related Classes of com.hp.hpl.jena.sparql.engine.QueryExecutionBase$TimeoutCallback

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.