/****************************************************************************** * Copyright (c) 2016 Oracle * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Konstantin Komissarchik - initial implementation and ongoing maintenance ******************************************************************************/ package org.eclipse.sapphire; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; /** * A generic queue for processing jobs. * * <p>Used by {@link ListenerContext} to deliver events to listeners. A single {@link JobQueue} can be shared by multiple * {@link ListenerContext} instances in order to synchronize event delivery.</p> * * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public final class JobQueue<T extends Runnable> { private final Queue<T> queue = new ConcurrentLinkedQueue<T>(); private final List<Filter<T>> filters = new CopyOnWriteArrayList<Filter<T>>(); /** * Adds a job to the end of the queue. * * @param job the job * @throws IllegalArgumentException if the job is null */ public void add( final T job ) { if( job == null ) { throw new IllegalArgumentException(); } this.queue.add( job ); } /** * Processes all of the jobs in the queue until the queue is empty or all of the remaining jobs are suspended * through the attached filters. */ public void process() { List<T> skipped = null; for( T job = this.queue.poll(); job != null; job = this.queue.poll() ) { boolean skip = false; for( final Filter<T> filter : this.filters ) { try { if( ! filter.allows( job ) ) { skip = true; break; } } catch( Exception e ) { Sapphire.service( LoggingService.class ).log( e ); } } if( skip ) { if( skipped == null ) { skipped = new ArrayList<T>(); } skipped.add( job ); } else { try { job.run(); } catch( Exception e ) { Sapphire.service( LoggingService.class ).log( e ); } } } if( skipped != null ) { this.queue.addAll( skipped ); } } /** * Removes jobs from the queue that are rejected by the filter. * * @param filter the filter * @throws IllegalArgumentException if the filter is null */ public void prune( final Filter<T> filter ) { if( filter == null ) { throw new IllegalArgumentException(); } for( final Iterator<T> itr = this.queue.iterator(); itr.hasNext(); ) { if( ! filter.allows( itr.next() ) ) { itr.remove(); } } } /** * Suspends jobs that are rejected by the filter. The suspended jobs will again become available for * processing once the suspension is released. * * @param filter the filter * @return a handle that must be closed to release the suspension * @throws IllegalArgumentException if the filter is null */ public Suspension suspend( final Filter<T> filter ) { if( filter == null ) { throw new IllegalArgumentException(); } final SuspensionFilter suspension = new SuspensionFilter( filter ); this.filters.add( suspension ); return suspension; } private final class SuspensionFilter extends Suspension implements Filter<T> { private final Filter<T> base; public SuspensionFilter( final Filter<T> base ) { this.base = base; } @Override public boolean allows( final T element ) { return this.base.allows( element ); } @Override public void dispose() { JobQueue.this.filters.remove( this ); } } }