/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun * Microsystems, Inc. All Rights Reserved. */ /* * StreamPool.java * * Created on March 2, 2001, 9:21 AM */ package org.openide.filesystems; import org.openide.util.*; import java.util.*; import java.io.*; /** * This class keeps info about streams (these streams are registered) that was * not closed yet. Also for every issued stream is hold stracktrace. * Sometimes there is necessary to know who didn`t close stream. * * @author rmatous * @version */ final class StreamPool extends Object { private static Map fo2StreamPool = new WeakHashMap (); private static Map fs2StreamPool = new WeakHashMap (); private Set iStreams; private Set oStreams; /** Creates new StreamPool */ private StreamPool() { } /** * This method creates subclassed NotifyInputStream (extends InputStream). * NotifyInputStream saves stacktrace in constrcuctor (creates new Exception) that * is used in method annotate. * This method also register this NotifyInputStream as * mapping (AbstractFolder, NotifyInputStream) and * mapping (AbstractFolder.getFileSystem(), NotifyInputStream). * If NotifyInputStream is closed then registration is freed. * For fo is also created StreamPool unless it exists yet. * @param fo FileObject that issues is * @param InputStream that should be issued * @return subclassed InputStream that is registered as mentioned above */ public static synchronized InputStream createInputStream (AbstractFolder fo, InputStream is) { InputStream retVal = new NotifyInputStream (fo, is); get (fo).iStream ().add (retVal); get (fo.getFileSystem()).iStream ().add (retVal); return retVal; } /** This method creates subclassed NotifyOutputStream (extends OutputStream). * NotifyOutputStream saves stacktrace in constrcuctor (creates new Exception) that * is used in method annotate. * This method also register this NotifyOutputStream as * mapping (AbstractFolder, NotifyOutputStream) and * mapping (AbstractFolder.getFileSystem(), NotifyOutputStream). * If NotifyOutputStream is closed then registration is freed. * For fo is also created StreamPool unless it exists yet. * @return subclassed OutputStream that is registered as mentioned above * @param fireFileChanged defines if should be fired fileChanged event after close of stream * @param fo FileObject that issues is * @param os OutputStream that should be issued */ public static synchronized OutputStream createOutputStream (AbstractFolder fo, OutputStream os, boolean fireFileChanged) { OutputStream retVal = new NotifyOutputStream (fo, os, fireFileChanged); get (fo).oStream ().add (retVal); get (fo.getFileSystem()).oStream ().add (retVal); return retVal; } /** * This method finds StreamPool assiciated with fo or null. This StreamPool is * created by means of createInputStream or createOutputStream. * @param fo FileObject whose StreamPool is looked for * @return StreamPool or null*/ public static synchronized StreamPool find (FileObject fo) { return (StreamPool) fo2StreamPool.get (fo); } /** * This method finds StreamPool assiciated with fs or null. This StreamPool is * created by means of createInputStream or createOutputStream. * @param fs FileSystem whose StreamPool is looked for * @return StreamPool or null*/ public static synchronized StreamPool find (FileSystem fs) { return (StreamPool) fs2StreamPool.get (fs); } /** * Annotates ex with all exceptions of unclosed streams. * @param ex that should be annotated */ public void annotate (Exception ex) { synchronized (StreamPool.class) { if (iStreams != null) { Iterator itIs = iStreams.iterator (); NotifyInputStream nis; while (itIs.hasNext()) { nis = (NotifyInputStream) itIs.next(); Exception annotation = nis.getException (); if (annotation != null) ExternalUtil.annotate (ex,annotation); } } if (oStreams != null) { Iterator itOs = oStreams.iterator (); NotifyOutputStream nos; while (itOs.hasNext()) { nos = (NotifyOutputStream) itOs.next(); Exception annotation = nos.getException (); if (annotation != null) ExternalUtil.annotate (ex,annotation); } } } } /** * @return true if there is any InputStream that was not closed yet */ public boolean isInputStreamOpen () { return iStreams != null && !iStreams.isEmpty (); } /** * @return true if there is any OutputStream that was not closed yet */ public boolean isOutputStreamOpen () { return oStreams != null && !oStreams.isEmpty (); } /** All next methods are private (Not visible outside this class)*/ private static StreamPool get (FileObject fo) { StreamPool strPool = (StreamPool) fo2StreamPool.get (fo); if (strPool == null) fo2StreamPool.put( fo, strPool = new StreamPool ()); return strPool; } private static StreamPool get (FileSystem fs) { StreamPool strPool = (StreamPool) fs2StreamPool.get (fs); if (strPool == null) fs2StreamPool.put( fs, strPool = new StreamPool ()); return strPool; } private Set iStream () { if (iStreams == null) iStreams = new WeakSet (); return iStreams; } private Set oStream () { if (oStreams == null) oStreams = new WeakSet (); return oStreams; } /** fireFileChange defines if should be fired fileChanged event after close of stream*/ private static void closeOutputStream (AbstractFolder fo, OutputStream os, boolean fireFileChanged) { StreamPool foPool = find (fo); StreamPool fsPool = find (fo.getFileSystem ()); Set foSet = (foPool != null)? foPool.oStreams : null; Set fsSet = (fsPool != null)? fsPool.oStreams : null; removeStreams (fsSet, foSet, os); removeStreamPools (fsPool, foPool, fo); fo.outputStreamClosed (fireFileChanged); } private static void closeInputStream (AbstractFolder fo, InputStream is) { StreamPool foPool = find (fo); StreamPool fsPool = find (fo.getFileSystem ()); Set foSet = (foPool != null)? foPool.iStreams : null; Set fsSet = (fsPool != null)? fsPool.iStreams : null; removeStreams (fsSet, foSet, is); removeStreamPools (fsPool, foPool, fo); } private static synchronized void removeStreams (Set fsSet,Set foSet,Object stream) { if (foSet != null) foSet.remove(stream); if (fsSet != null) fsSet.remove(stream); } private static synchronized void removeStreamPools (StreamPool fsPool,StreamPool foPool,AbstractFolder fo) { boolean isIStreamEmpty = (foPool == null || foPool.iStreams == null || foPool.iStreams.isEmpty()); boolean isOStreamEmpty = (foPool == null || foPool.oStreams == null || foPool.oStreams.isEmpty()); if (isIStreamEmpty && isOStreamEmpty) fo2StreamPool.remove(fo); isIStreamEmpty = (fsPool == null || fsPool.iStreams == null || fsPool.iStreams.isEmpty()); isOStreamEmpty = (fsPool == null || fsPool.oStreams == null || fsPool.oStreams.isEmpty()); if (isIStreamEmpty && isOStreamEmpty) fs2StreamPool.remove(fo.getFileSystem ()); } static final class NotifyOutputStream extends FilterOutputStream { private Exception ex; AbstractFolder fo; /** defines if should be fired fileChanged event after close of stream */ private boolean fireFileChanged; public NotifyOutputStream (AbstractFolder fo, OutputStream os, boolean fireFileChanged) { super (os); this.fo = fo; ex = new Exception (); this.fireFileChanged = fireFileChanged; } /** Faster implementation of writing than is implemented in * the filter output stream. */ public void write (byte[] b, int off, int len) throws IOException { out.write(b, off, len); } public void close () throws IOException { if (ex != null) { ex = null; super.close (); closeOutputStream (fo, this, fireFileChanged); } } public Exception getException () { return ex; } } static final class NotifyInputStream extends FilterInputStream { private Exception ex; AbstractFolder fo; public NotifyInputStream (AbstractFolder fo, InputStream is) { super (is); this.fo = fo; ex = new Exception (); } public void close () throws IOException { if (ex != null) { ex = null; super.close (); closeInputStream (fo, this); } } public Exception getException () { return ex; } } }