/* * 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.sun.jini.mercury; import net.jini.id.Uuid; import net.jini.core.event.RemoteEvent; import java.io.IOException; import com.sun.jini.logging.Levels; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.logging.Level; import java.util.logging.Logger; import java.util.NoSuchElementException; /** * Class that implements the interface for an <tt>EventLog</tt>. * This class encapsulates the details of reading/writing events from/to * some non-persistent mechanism. * * This class makes certain assumptions. First, the <tt>next</tt> and * <tt>remove</tt> methods are intended to be called in pairs. If * <tt>remove</tt> is not called, then subsequent calls to <tt>next</tt> * will attempt to return the same object. Calling <tt>remove</tt> * essentially advances the read pointer to the next object, if any. * * There is also an implicit assumption of external synchronization by the * caller. That is, only one calling thread will be accessing the log at a time. * * @author Sun Microsystems, Inc. * * @since 2.0 */ class TransientEventLog implements EventLog { // // Class fields // /** <tt>Logger</tt> used for persistence-related debugging messages */ private static final Logger persistenceLogger = MailboxImpl.persistenceLogger; // // Object fields // /** The associated <tt>Uuid</tt> for this <tt>EventLog</tt>. */ private Uuid uuid = null; /** The associated, non-persistent storage for events */ private List entries = null; /** * Flag that is used to determine whether or not this object * has been closed. */ private boolean closed = false; /** * Flag that is used to determine whether or not this object * has been initialized. */ private boolean initialized = false; /** * Helper class used to hold a remote event and a sequence id. */ private static class RemoteEventHolder { private final long id; private final RemoteEvent remoteEvent; RemoteEventHolder(long stamp, RemoteEvent re) { id = stamp; remoteEvent = re; } long getID() { return id; } RemoteEvent getRemoteEvent() { return remoteEvent; } } /** * Counter used to produce event ids. */ private long eventCounter = 1L; /** * Simple constructor that takes a <tt>Uuid</tt> argument. * * @exception IllegalArgumentException if the argument is null */ TransientEventLog(Uuid uuid) { if (uuid == null) throw new IllegalArgumentException("Uuid cannot be null"); this.uuid = uuid; entries = Collections.synchronizedList(new LinkedList()); if (persistenceLogger.isLoggable(Level.FINEST)) { persistenceLogger.log(Level.FINEST, "TransientEventLog for: {0}", uuid); } } // Inherit documentation from supertype public void init() throws IOException { if (initialized) throw new InternalMailboxException( "Trying to re-initialize event log " + "for: " + uuid); initialized = true; } /** * Asserts that the log is in a valid state. * * @exception IOException if the log is in an invalid state */ private void stateCheck() throws IOException { if (!initialized) throw new IOException("Trying to use an uninitialized " + "event log for: " + uuid); if (closed) throw new IOException("Attempt to access closed log file for : " + uuid); } // Inherit documentation from supertype public void add(RemoteEvent event) throws IOException { stateCheck(); long id = eventCounter++; RemoteEventHolder data = new RemoteEventHolder(id, event); entries.add(data); printControlData(persistenceLogger, "TransientEventLog::add"); } // Inherit documentation from supertype public RemoteEvent next() throws IOException { stateCheck(); // Check if empty if (isEmpty()) throw new NoSuchElementException(); printControlData(persistenceLogger, "TransientEventLog::next"); RemoteEventHolder data = (RemoteEventHolder)entries.get(0); return (RemoteEvent)data.getRemoteEvent(); } // Inherit documentation from supertype public RemoteEventData[] readAhead(int maxEvents) throws IOException { stateCheck(); if (maxEvents < 0) throw new IllegalArgumentException(); if (maxEvents == 0) return new RemoteEventData[0]; // Check if empty if (isEmpty()) throw new NoSuchElementException(); printControlData(persistenceLogger, "TransientEventLog::readAhead"); int limit = (maxEvents < entries.size())?maxEvents:entries.size(); RemoteEventHolder[] evts = (RemoteEventHolder[]) entries.subList(0, limit).toArray(new RemoteEventHolder[0]); RemoteEventData[] set = new RemoteEventData[evts.length]; for (int i=0; i<set.length; i++) { set[i] = new RemoteEventData( evts[i].getRemoteEvent(), new Long(evts[i].getID())); } return set; } // Inherit documentation from supertype public boolean isEmpty() throws IOException { stateCheck(); return entries.isEmpty(); } // Inherit documentation from supertype public void remove() throws IOException { stateCheck(); try { entries.remove(0); } catch (IndexOutOfBoundsException iob) { throw new NoSuchElementException(); } printControlData(persistenceLogger, "TransientEventLog::remove"); } // Inherit documentation from supertype public void moveAhead(Object cookie) throws IOException { stateCheck(); if (cookie == null) return; if (persistenceLogger.isLoggable(Level.FINEST)) { persistenceLogger.log(Level.FINEST, "moveAhead past {0}", cookie); } // TODO - trap ClassCastException and throw? long lastID = ((Long)cookie).longValue(); if (lastID >= eventCounter) { throw new NoSuchElementException(); } RemoteEventHolder rh = null; ListIterator iter = entries.listIterator(); while (iter.hasNext()) { rh = (RemoteEventHolder)iter.next(); if (rh.getID() <= lastID) { iter.remove(); if (persistenceLogger.isLoggable(Level.FINEST)) { persistenceLogger.log(Level.FINEST, "Removing event with ID {0}", new Long(rh.getID())); } } else { break; } } printControlData(persistenceLogger, "TransientEventLog::moveAhead"); } // Inherit documentation from supertype public void close() throws IOException { stateCheck(); closed = true; if (persistenceLogger.isLoggable(Level.FINEST)) { persistenceLogger.log(Level.FINEST, "TransientEventLog::close for {0}", uuid); } // Do nothing } // Inherit documentation from supertype public void delete() throws IOException { if (!closed) throw new IOException("Cannot delete log until it is closed"); entries.clear(); if (persistenceLogger.isLoggable(Level.FINEST)) { persistenceLogger.log(Level.FINEST, "TransientEventLog::destroy for {0}", uuid); } } /** * Output state information to the given <tt>Logger</tt>. * This is intended for debugging purposes only. */ private void printControlData(Logger logger, String msg) { if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "{0}", msg); logger.log(Level.FINEST, "ID: {0}", uuid); logger.log(Level.FINEST, "NumEvents: {0}", new Long(entries.size())); } } }