/** * 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 org.apache.hadoop.gateway.audit.log4j.appender; import jdbm.RecordManager; import jdbm.RecordManagerFactory; import jdbm.htree.HTree; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.Properties; public class JdbmQueue<E> { private static final String STAT_NAME = "stat"; private static final String DATA_TREE = "data"; private RecordManager db; private long stat; private HTree data; private boolean open; public JdbmQueue( File file ) throws IOException { Properties props = new Properties(); db = RecordManagerFactory.createRecordManager( file.getAbsolutePath(), props ); stat = findStat(); data = getData(); db.commit(); open = true; } public synchronized void enqueue( E e ) throws IOException { boolean committed = false; try { Stat stat = getStat(); stat.lastEnqueue++; setStat( stat ); data.put( stat.lastEnqueue, e ); db.commit(); committed = true; notify(); } finally { if( !committed ) { db.rollback(); } } } public synchronized E dequeue() throws InterruptedException, IOException { boolean committed = false; try { Stat s = getStat(); while( open && s.size() == 0 ) { wait(); if( !open ) { return null; } s = getStat(); } s.nextDequeue++; Long key = Long.valueOf( s.nextDequeue ); @SuppressWarnings("unchecked") E e = (E)data.get( key ); data.remove( key ); db.update( stat, s ); db.commit(); committed = true; return e; } finally { if( !committed && open ) { db.rollback(); } } } public synchronized boolean process( Consumer<E> consumer ) throws IOException { boolean committed = false; try { E e = dequeue(); boolean consumed = consumer.consume( e ); if( consumed && open ) { db.commit(); committed = true; } } catch( RuntimeException e ) { throw e; } catch( IOException e ) { throw e; } catch( Throwable t ) { throw new RuntimeException( t ); } finally { if( !committed && open ) { db.rollback(); } } return committed; } public synchronized void stop() { open = false; notifyAll(); } public synchronized void close() throws IOException { stop(); db.close(); } long findStat() throws IOException { long recid = db.getNamedObject( STAT_NAME ); if ( recid == 0 ) { recid = db.insert( new Stat() ); db.setNamedObject( STAT_NAME, recid ); } return recid; } Stat getStat() throws IOException { Stat stat; long recid = db.getNamedObject( STAT_NAME ); if ( recid == 0 ) { stat = new Stat(); db.setNamedObject( STAT_NAME, db.insert( stat ) ); } else { stat = (Stat)db.fetch( recid ); } return stat; } void setStat( Stat update ) throws IOException { db.update( stat, update ); } HTree getData() throws IOException { HTree tree; long recid = db.getNamedObject( DATA_TREE ); if ( recid != 0 ) { tree = HTree.load( db, recid ); } else { tree = HTree.createInstance( db ); db.setNamedObject( DATA_TREE, tree.getRecid() ); } return tree; } private static final class Stat implements Serializable { private static final long serialVersionUID = 1L; private long lastEnqueue = 0; private long nextDequeue = 0; private long size() { return lastEnqueue - nextDequeue; } } public interface Consumer<E> { boolean consume( E e ); } }