1 19 package org.openide.filesystems; 20 21 import org.openide.util.*; 22 23 import java.io.*; 24 25 import java.util.*; 26 27 28 35 final class StreamPool extends Object { 36 37 private static final boolean ANNOTATE_UNCLOSED_STREAMS = Boolean.getBoolean( 38 "org.openide.filesystems.annotateUnclosedStreams" 39 ); private static Map<FileObject, StreamPool> fo2StreamPool = new WeakHashMap<FileObject, StreamPool>(); 41 private static Map<FileSystem, StreamPool> fs2StreamPool = new WeakHashMap<FileSystem, StreamPool>(); 42 private Set<InputStream> iStreams; 43 private Set<OutputStream> oStreams; 44 45 46 private StreamPool() { 47 } 48 49 60 public static InputStream createInputStream(final AbstractFolder fo) 61 throws FileNotFoundException { 62 InputStream retVal = null; 63 64 synchronized (StreamPool.class) { 65 try { 66 get(fo).waitForOutputStreamsClosed(2000); 67 retVal = new NotifyInputStream(fo); 68 get(fo).iStream().add(retVal); 69 get(fo.getFileSystem()).iStream().add(retVal); 70 } catch (InterruptedException e) { 71 ExternalUtil.exception(e); 72 } 73 } 74 75 if ((retVal != null) && (retVal instanceof NotifyInputStream)) { 76 AbstractFileSystem abstractFileSystem = ((AbstractFileSystem) fo.getFileSystem()); 77 ((NotifyInputStream) retVal).setOriginal(abstractFileSystem.info.inputStream(fo.getPath())); 78 } else { 79 retVal = new InputStream() { 80 public int read() throws IOException { 81 FileAlreadyLockedException alreadyLockedEx = new FileAlreadyLockedException(fo.getPath()); 82 get(fo).annotate(alreadyLockedEx); 83 throw alreadyLockedEx; 84 } 85 }; 86 } 87 88 return retVal; 89 } 90 91 103 public static OutputStream createOutputStream(final AbstractFolder fo, boolean fireFileChanged) 104 throws IOException { 105 OutputStream retVal = null; 106 107 synchronized (StreamPool.class) { 108 try { 109 get(fo).waitForInputStreamsClosed(2000); 110 get(fo).waitForOutputStreamsClosed(2000); 111 112 retVal = new NotifyOutputStream(fo, fireFileChanged); 113 get(fo).oStream().add(retVal); 114 get(fo.getFileSystem()).oStream().add(retVal); 115 } catch (InterruptedException e) { 116 ExternalUtil.exception(e); 117 } 118 } 119 120 if ((retVal != null) && (retVal instanceof NotifyOutputStream)) { 121 AbstractFileSystem abstractFileSystem = ((AbstractFileSystem) fo.getFileSystem()); 122 ((NotifyOutputStream) retVal).setOriginal(abstractFileSystem.info.outputStream(fo.getPath())); 123 } else { 124 retVal = new OutputStream() { 125 public void write(int b) throws IOException { 126 FileAlreadyLockedException alreadyLockedEx = new FileAlreadyLockedException(fo.getPath()); 127 get(fo).annotate(alreadyLockedEx); 128 throw alreadyLockedEx; 129 } 130 }; 131 } 132 133 return retVal; 134 } 135 136 141 public static synchronized StreamPool find(FileObject fo) { 142 return fo2StreamPool.get(fo); 143 } 144 145 150 public static synchronized StreamPool find(FileSystem fs) { 151 return fs2StreamPool.get(fs); 152 } 153 154 157 public void annotate(Exception ex) { 158 if (!ANNOTATE_UNCLOSED_STREAMS) { 159 return; 160 } 161 162 synchronized (StreamPool.class) { 163 if (iStreams != null) { 164 Iterator itIs = iStreams.iterator(); 165 NotifyInputStream nis; 166 167 while (itIs.hasNext()) { 168 nis = (NotifyInputStream) itIs.next(); 169 170 Exception annotation = nis.getException(); 171 172 if (annotation != null) { 173 ExternalUtil.annotate(ex, annotation); 174 } 175 } 176 } 177 178 if (oStreams != null) { 179 Iterator itOs = oStreams.iterator(); 180 NotifyOutputStream nos; 181 182 while (itOs.hasNext()) { 183 nos = (NotifyOutputStream) itOs.next(); 184 185 Exception annotation = nos.getException(); 186 187 if (annotation != null) { 188 ExternalUtil.annotate(ex, annotation); 189 } 190 } 191 } 192 } 193 } 194 195 197 public boolean isInputStreamOpen() { 198 return (iStreams != null) && !iStreams.isEmpty(); 199 } 200 201 private void waitForInputStreamsClosed(int timeInMs) 202 throws InterruptedException { 203 synchronized (StreamPool.class) { 204 if (isInputStreamOpen()) { 205 StreamPool.class.wait(timeInMs); 206 207 if (isInputStreamOpen()) { 208 throw new InterruptedException (); 209 } 210 } 211 } 212 } 213 214 private void waitForOutputStreamsClosed(int timeInMs) 215 throws InterruptedException { 216 synchronized (StreamPool.class) { 217 if (isOutputStreamOpen()) { 218 StreamPool.class.wait(timeInMs); 219 220 if (isOutputStreamOpen()) { 221 throw new InterruptedException (); 222 } 223 } 224 } 225 } 226 227 229 public boolean isOutputStreamOpen() { 230 return (oStreams != null) && !oStreams.isEmpty(); 231 } 232 233 234 private static StreamPool get(FileObject fo) { 235 StreamPool strPool = fo2StreamPool.get(fo); 236 237 if (strPool == null) { 238 fo2StreamPool.put(fo, strPool = new StreamPool()); 239 } 240 241 return strPool; 242 } 243 244 private static StreamPool get(FileSystem fs) { 245 StreamPool strPool = fs2StreamPool.get(fs); 246 247 if (strPool == null) { 248 fs2StreamPool.put(fs, strPool = new StreamPool()); 249 } 250 251 return strPool; 252 } 253 254 private Set<InputStream> iStream() { 255 if (iStreams == null) { 256 iStreams = new WeakSet<InputStream>(); 257 } 258 259 return iStreams; 260 } 261 262 private Set<OutputStream> oStream() { 263 if (oStreams == null) { 264 oStreams = new WeakSet<OutputStream>(); 265 } 266 267 return oStreams; 268 } 269 270 271 private static void closeOutputStream(AbstractFolder fo, OutputStream os, boolean fireFileChanged) { 272 StreamPool foPool = find(fo); 273 StreamPool fsPool = find(fo.getFileSystem()); 274 Set foSet = (foPool != null) ? foPool.oStreams : null; 275 Set fsSet = (fsPool != null) ? fsPool.oStreams : null; 276 277 removeStreams(fsSet, foSet, os); 278 removeStreamPools(fsPool, foPool, fo); 279 fo.outputStreamClosed(fireFileChanged); 280 } 281 282 private static void closeInputStream(AbstractFolder fo, InputStream is) { 283 StreamPool foPool = find(fo); 284 StreamPool fsPool = find(fo.getFileSystem()); 285 Set foSet = (foPool != null) ? foPool.iStreams : null; 286 Set fsSet = (fsPool != null) ? fsPool.iStreams : null; 287 288 removeStreams(fsSet, foSet, is); 289 removeStreamPools(fsPool, foPool, fo); 290 } 291 292 private static synchronized void removeStreams(Set fsSet, Set foSet, Object stream) { 293 if (foSet != null) { 294 foSet.remove(stream); 295 } 296 297 if (fsSet != null) { 298 fsSet.remove(stream); 299 } 300 } 301 302 private static synchronized void removeStreamPools(StreamPool fsPool, StreamPool foPool, AbstractFolder fo) { 303 boolean isIStreamEmpty = ((foPool == null) || (foPool.iStreams == null) || foPool.iStreams.isEmpty()); 304 boolean isOStreamEmpty = ((foPool == null) || (foPool.oStreams == null) || foPool.oStreams.isEmpty()); 305 306 if (isIStreamEmpty && isOStreamEmpty) { 307 fo2StreamPool.remove(fo); 308 } 309 310 isIStreamEmpty = ((fsPool == null) || (fsPool.iStreams == null) || fsPool.iStreams.isEmpty()); 311 isOStreamEmpty = ((fsPool == null) || (fsPool.oStreams == null) || fsPool.oStreams.isEmpty()); 312 313 if (isIStreamEmpty && isOStreamEmpty) { 314 fs2StreamPool.remove(fo.getFileSystem()); 315 } 316 } 317 318 private static final class NotifyOutputStream extends FilterOutputStream { 319 private static final OutputStream emptyOs = new ByteArrayOutputStream(); 320 private Exception ex; 321 private boolean closed = false; 322 AbstractFolder fo; 323 324 325 private boolean fireFileChanged; 326 327 public NotifyOutputStream(AbstractFolder fo, boolean fireFileChanged) { 328 super(emptyOs); 329 this.fo = fo; 330 331 if (ANNOTATE_UNCLOSED_STREAMS) { 332 ex = new Exception (); 333 } 334 335 this.fireFileChanged = fireFileChanged; 336 } 337 338 private void setOriginal(OutputStream os) { 339 out = os; 340 } 341 342 345 public void write(byte[] b, int off, int len) throws IOException { 346 out.write(b, off, len); 347 } 348 349 public void close() throws IOException { 350 if (!closed) { 351 closed = true; 352 ex = null; 353 super.out.flush(); 354 super.close(); 355 closeOutputStream(fo, this, fireFileChanged); 356 357 synchronized (StreamPool.class) { 358 StreamPool.class.notifyAll(); 359 } 360 } 361 } 362 363 public Exception getException() { 364 return ex; 365 } 366 } 367 368 private static final class NotifyInputStream extends FilterInputStream { 369 private static final InputStream emptyIs = new ByteArrayInputStream(new byte[0]); 370 private Exception ex; 371 AbstractFolder fo; 372 private boolean closed = false; 373 374 public NotifyInputStream(AbstractFolder fo) { 375 super(emptyIs); 376 this.fo = fo; 377 378 if (ANNOTATE_UNCLOSED_STREAMS) { 379 ex = new Exception (); 380 } 381 } 382 383 private void setOriginal(InputStream is) { 384 in = is; 385 } 386 387 public void close() throws IOException { 388 if (!closed) { 389 closed = true; 390 ex = null; 391 super.close(); 392 closeInputStream(fo, this); 393 394 synchronized (StreamPool.class) { 395 if (!StreamPool.get(fo).isInputStreamOpen()) { 396 StreamPool.class.notifyAll(); 397 } 398 } 399 } 400 } 401 402 public Exception getException() { 403 return ex; 404 } 405 } 406 } 407 | Popular Tags |