/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.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://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */
package com.sun.jini.mercury;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.SyncFailedException;
import java.rmi.MarshalledObject;
import net.jini.core.event.RemoteEvent;

/**
 * This class provides the interface for writing <tt>RemoteEvent</tt>s to 
 * a given <tt>LogOutputStream</tt>.
 *
 * @author Sun Microsystems, Inc.
 *
 * @since 1.1
 */
class EventWriter {
    
    /**
     * This class extends <tt>ObjectOutputStream</tt> in order to obtain
     * object writing methods. This class is used in conjunction with 
     * <tt>SwitchOutputStream</tt> in order to provide a re-targetable
     * output stream capability.
     */
    private static class EventOutputStream extends ObjectOutputStream {
	
	/**
	 * Simple constructor that passes its argument to the superclass
         *
         * @exception IOException if an I/O error occurs
	 */
	public EventOutputStream(OutputStream out) throws IOException {
	    super(out);
	}
	
	/**
	 * Overrides <tt>ObjectOutputStream</tt>'s method with no-op
	 * functionality.  This prevents the underlying stream from being
	 * being accessed during construction. See <tt>SwitchOutputStream</tt>.
         *
         * @exception IOException if an I/O error occurs
	 */
	protected void writeStreamHeader() throws IOException {
	    // Do nothing
	}
    }

    /**
     * This class is intended to be the <tt>OutputStream</tt> provided
     * to <tt>EventOutputStream</tt>'s constructor. This class essentially
     * delegates <tt>OutputStream</tt> functionality to the provided
     * <tt>LogOutputStream</tt>. The <tt>setOutputStream</tt> method
     * allows the underlying <tt>LogOutputStream</tt> to be re-targeted
     * at runtime. 
     */
    private static class SwitchOutputStream extends OutputStream {
	
	/** The delegation target for the <tt>OutputStream</tt> methods */
	private LogOutputStream out;

        /**
         * Simple constructor that assigns the given argument to the
         * appropriate internal field.
         */
	public SwitchOutputStream(LogOutputStream out) {
	    this.out = out;
	}
	
	// documentation inherited from supertype
	public void write(int b) throws IOException {
	    out.write(b);
	}
	
	// documentation inherited from supertype
	public void write(byte[] b) throws IOException {
	    out.write(b);
	}
	
	// documentation inherited from supertype
	public void write(byte[] b, int off, int len) throws IOException {
	    out.write(b, off, len);
	}
	
	// documentation inherited from supertype
	public void flush() throws IOException {
	    out.flush();
	}
	
	// documentation inherited from supertype
	public void close() throws IOException {
	    out.close();
	}
	
	/** Sets the delegation target */
	public void setOutputStream(LogOutputStream out) {
	    this.out = out;
	}

    }
    
    /** Reference to <tt>EventOutputStream</tt> for this class */
    private EventOutputStream eout;

    /** Reference to <tt>SwitchOutputStream</tt> for this class */
    private SwitchOutputStream sout;

    /**
     * Simple constructor that creates the appropriate internal objects.
     *
     * @exception IOException if an I/O error occurs
     */
    public EventWriter() throws IOException {
	// default delegation target is null
	sout = new SwitchOutputStream(null);

	// Use the "switchable" output stream as the target
	eout = new EventOutputStream(sout);
    }
    
    /**
     * Writes the given <tt>RemoteEvent</tt> to the provided
     * <tt>LogOutputStream</tt>. It also attempts to synchronize
     * the write with the underlying storage mechanism.
     *
     * @exception IOException 
     *        Thrown if an I/O error occurs
     * @exception SyncFailedException
     *	      Thrown when the buffers cannot be flushed,
     *	      or because the system cannot guarantee that all the
     *	      buffers have been synchronized with physical media. 
     */
    public void write(RemoteEvent ev, LogOutputStream out) 
        throws IOException, SyncFailedException
    {
        // Set stream target
	sout.setOutputStream(out);
	try {
	    // Wrap the event as a MarshalledObject so that
	    // we can ensure a successful read later on (i.e.
	    // should always be able to read MarshalledObject
	    // but we might not be able to reconstruct a 
	    // RemoteEvent object because of codebase problems).
	    MarshalledObject mo = new MarshalledObject(ev);
	    eout.reset();
	    eout.writeObject(mo);
	    eout.flush();
	    out.sync();
	} finally {
	    // reset target to null
	    sout.setOutputStream(null);
	}
    }
}
