1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. 5 * 6 * The contents of this file are subject to the terms of either the GNU 7 * General Public License Version 2 only ("GPL") or the Common Development 8 * and Distribution License("CDDL") (collectively, the "License"). You 9 * may not use this file except in compliance with the License. You can 10 * obtain a copy of the License at 11 * https://oss.oracle.com/licenses/CDDL+GPL-1.1 12 * or LICENSE.txt. See the License for the specific 13 * language governing permissions and limitations under the License. 14 * 15 * When distributing the software, include this License Header Notice in each 16 * file and include the License file at LICENSE.txt. 17 * 18 * GPL Classpath Exception: 19 * Oracle designates this particular file as subject to the "Classpath" 20 * exception as provided by Oracle in the GPL Version 2 section of the License 21 * file that accompanied this code. 22 * 23 * Modifications: 24 * If applicable, add the following below the License Header, with the fields 25 * enclosed by brackets [] replaced by your own identifying information: 26 * "Portions Copyright [year] [name of copyright owner]" 27 * 28 * Contributor(s): 29 * If you wish your version of this file to be governed by only the CDDL or 30 * only the GPL Version 2, indicate your decision by adding "[Contributor] 31 * elects to include this software in this distribution under the [CDDL or GPL 32 * Version 2] license." If you don't indicate a single choice of license, a 33 * recipient has the option to distribute your version of this file under 34 * either the CDDL, the GPL Version 2 or to extend the choice of license to 35 * its licensees as provided above. However, if you add GPL Version 2 code 36 * and therefore, elected the GPL Version 2 license, then the option applies 37 * only if the new code is made subject to such option by the copyright 38 * holder. 39 */ 40 41 package javax.mail; 42 43 import java.lang.*; 44 import java.util.ArrayList; 45 import java.util.EventListener; 46 import java.util.List; 47 import java.util.Vector; 48 import java.util.concurrent.Executor; 49 import javax.mail.search.SearchTerm; 50 import javax.mail.event.*; 51 52 /** 53 * Folder is an abstract class that represents a folder for mail 54 * messages. Subclasses implement protocol specific Folders.<p> 55 * 56 * Folders can contain Messages, other Folders or both, thus providing 57 * a tree-like hierarchy rooted at the Store's default folder. (Note 58 * that some Folder implementations may not allow both Messages and 59 * other Folders in the same Folder).<p> 60 * 61 * The interpretation of folder names is implementation dependent. 62 * The different levels of hierarchy in a folder's full name 63 * are separated from each other by the hierarchy delimiter 64 * character.<p> 65 * 66 * The case-insensitive full folder name (that is, the full name 67 * relative to the default folder for a Store) <strong>INBOX</strong> 68 * is reserved to mean the "primary folder for this user on this 69 * server". Not all Stores will provide an INBOX folder, and not 70 * all users will have an INBOX folder at all times. The name 71 * <strong>INBOX</strong> is reserved to refer to this folder, 72 * when it exists, in Stores that provide it. <p> 73 * 74 * A Folder object obtained from a Store need not actually exist 75 * in the backend store. The <code>exists</code> method tests whether 76 * the folder exists or not. The <code>create</code> method 77 * creates a Folder. <p> 78 * 79 * A Folder is initially in the closed state. Certain methods are valid 80 * in this state; the documentation for those methods note this. A 81 * Folder is opened by calling its 'open' method. All Folder methods, 82 * except <code>open</code>, <code>delete</code> and 83 * <code>renameTo</code>, are valid in this state. <p> 84 * 85 * The only way to get a Folder is by invoking the 86 * <code>getFolder</code> method on Store, Folder, or Session, or by invoking 87 * the <code>list</code> or <code>listSubscribed</code> methods 88 * on Folder. Folder objects returned by the above methods are not 89 * cached by the Store. Thus, invoking the <code>getFolder</code> method 90 * with the same folder name multiple times will return distinct Folder 91 * objects. Likewise for the <code>list</code> and <code>listSubscribed</code> 92 * methods. <p> 93 * 94 * The Message objects within the Folder are cached by the Folder. 95 * Thus, invoking <code>getMessage(msgno)</code> on the same message number 96 * multiple times will return the same Message object, until an 97 * expunge is done on this Folder. <p> 98 * 99 * Message objects from a Folder are only valid while a Folder is open 100 * and should not be accessed after the Folder is closed, even if the 101 * Folder is subsequently reopened. Instead, new Message objects must 102 * be fetched from the Folder after the Folder is reopened. <p> 103 * 104 * Note that a Message's message number can change within a 105 * session if the containing Folder is expunged using the expunge 106 * method. Clients that use message numbers as references to messages 107 * should be aware of this and should be prepared to deal with this 108 * situation (probably by flushing out existing message number references 109 * and reloading them). Because of this complexity, it is better for 110 * clients to use Message objects as references to messages, rather than 111 * message numbers. Expunged Message objects still have to be 112 * pruned, but other Message objects in that folder are not affected by the 113 * expunge. 114 * 115 * @author John Mani 116 * @author Bill Shannon 117 */ 118 119 public abstract class Folder implements AutoCloseable { 120 121 /** 122 * The parent store. 123 */ 124 protected Store store; 125 126 /** 127 * The open mode of this folder. The open mode is 128 * <code>Folder.READ_ONLY</code>, <code>Folder.READ_WRITE</code>, 129 * or -1 if not known. 130 * @since JavaMail 1.1 131 */ 132 protected int mode = -1; 133 134 /* 135 * The queue of events to be delivered. 136 */ 137 private final EventQueue q; 138 139 /** 140 * Constructor that takes a Store object. 141 * 142 * @param store the Store that holds this folder 143 */ Folder(Store store)144 protected Folder(Store store) { 145 this.store = store; 146 147 // create or choose the appropriate event queue 148 Session session = store.getSession(); 149 String scope = 150 session.getProperties().getProperty("mail.event.scope", "folder"); 151 Executor executor = 152 (Executor)session.getProperties().get("mail.event.executor"); 153 if (scope.equalsIgnoreCase("application")) 154 q = EventQueue.getApplicationEventQueue(executor); 155 else if (scope.equalsIgnoreCase("session")) 156 q = session.getEventQueue(); 157 else if (scope.equalsIgnoreCase("store")) 158 q = store.getEventQueue(); 159 else // if (scope.equalsIgnoreCase("folder")) 160 q = new EventQueue(executor); 161 } 162 163 /** 164 * Returns the name of this Folder. <p> 165 * 166 * This method can be invoked on a closed Folder. 167 * 168 * @return name of the Folder 169 */ getName()170 public abstract String getName(); 171 172 /** 173 * Returns the full name of this Folder. If the folder resides under 174 * the root hierarchy of this Store, the returned name is relative 175 * to the root. Otherwise an absolute name, starting with the 176 * hierarchy delimiter, is returned. <p> 177 * 178 * This method can be invoked on a closed Folder. 179 * 180 * @return full name of the Folder 181 */ getFullName()182 public abstract String getFullName(); 183 184 /** 185 * Return a URLName representing this folder. The returned URLName 186 * does <em>not</em> include the password used to access the store. 187 * 188 * @return the URLName representing this folder 189 * @exception MessagingException for failures 190 * @see URLName 191 * @since JavaMail 1.1 192 */ getURLName()193 public URLName getURLName() throws MessagingException { 194 URLName storeURL = getStore().getURLName(); 195 String fullname = getFullName(); 196 StringBuilder encodedName = new StringBuilder(); 197 198 if (fullname != null) { 199 /* 200 // We need to encode each of the folder's names. 201 char separator = getSeparator(); 202 StringTokenizer tok = new StringTokenizer( 203 fullname, new Character(separator).toString(), true); 204 205 while (tok.hasMoreTokens()) { 206 String s = tok.nextToken(); 207 if (s.charAt(0) == separator) 208 encodedName.append(separator); 209 else 210 // XXX - should encode, but since there's no decoder... 211 //encodedName.append(java.net.URLEncoder.encode(s)); 212 encodedName.append(s); 213 } 214 */ 215 // append the whole thing, until we can encode 216 encodedName.append(fullname); 217 } 218 219 /* 220 * Sure would be convenient if URLName had a 221 * constructor that took a base URLName. 222 */ 223 return new URLName(storeURL.getProtocol(), storeURL.getHost(), 224 storeURL.getPort(), encodedName.toString(), 225 storeURL.getUsername(), 226 null /* no password */); 227 } 228 229 /** 230 * Returns the Store that owns this Folder object. 231 * This method can be invoked on a closed Folder. 232 * 233 * @return the Store 234 */ getStore()235 public Store getStore() { 236 return store; 237 } 238 239 /** 240 * Returns the parent folder of this folder. 241 * This method can be invoked on a closed Folder. If this folder 242 * is the top of a folder hierarchy, this method returns null. <p> 243 * 244 * Note that since Folder objects are not cached, invoking this method 245 * returns a new distinct Folder object. 246 * 247 * @return Parent folder 248 * @exception MessagingException for failures 249 */ getParent()250 public abstract Folder getParent() throws MessagingException; 251 252 /** 253 * Tests if this folder physically exists on the Store. 254 * This method can be invoked on a closed Folder. 255 * 256 * @return true if the folder exists, otherwise false 257 * @see #create 258 * @exception MessagingException typically if the connection 259 * to the server is lost. 260 */ exists()261 public abstract boolean exists() throws MessagingException; 262 263 /** 264 * Returns a list of Folders belonging to this Folder's namespace 265 * that match the specified pattern. Patterns may contain the wildcard 266 * characters <code>"%"</code>, which matches any character except hierarchy 267 * delimiters, and <code>"*"</code>, which matches any character. <p> 268 * 269 * As an example, given the folder hierarchy: <pre> 270 * Personal/ 271 * Finance/ 272 * Stocks 273 * Bonus 274 * StockOptions 275 * Jokes 276 * </pre> 277 * <code>list("*")</code> on "Personal" will return the whole 278 * hierarchy. <br> 279 * <code>list("%")</code> on "Personal" will return "Finance" and 280 * "Jokes". <br> 281 * <code>list("Jokes")</code> on "Personal" will return "Jokes".<br> 282 * <code>list("Stock*")</code> on "Finance" will return "Stocks" 283 * and "StockOptions". <p> 284 * 285 * Folder objects are not cached by the Store, so invoking this 286 * method on the same pattern multiple times will return that many 287 * distinct Folder objects. <p> 288 * 289 * This method can be invoked on a closed Folder. 290 * 291 * @param pattern the match pattern 292 * @return array of matching Folder objects. An empty 293 * array is returned if no matching Folders exist. 294 * @see #listSubscribed 295 * @exception FolderNotFoundException if this folder does 296 * not exist. 297 * @exception MessagingException for other failures 298 */ list(String pattern)299 public abstract Folder[] list(String pattern) throws MessagingException; 300 301 /** 302 * Returns a list of subscribed Folders belonging to this Folder's 303 * namespace that match the specified pattern. If the folder does 304 * not support subscription, this method should resolve to 305 * <code>list</code>. 306 * (The default implementation provided here, does just this). 307 * The pattern can contain wildcards as for <code>list</code>. <p> 308 * 309 * Note that, at a given level of the folder hierarchy, a particular 310 * folder may not be subscribed, but folders underneath that folder 311 * in the folder hierarchy may be subscribed. In order to allow 312 * walking the folder hierarchy, such unsubscribed folders may be 313 * returned, indicating that a folder lower in the hierarchy is 314 * subscribed. The <code>isSubscribed</code> method on a folder will 315 * tell whether any particular folder is actually subscribed. <p> 316 * 317 * Folder objects are not cached by the Store, so invoking this 318 * method on the same pattern multiple times will return that many 319 * distinct Folder objects. <p> 320 * 321 * This method can be invoked on a closed Folder. 322 * 323 * @param pattern the match pattern 324 * @return array of matching subscribed Folder objects. An 325 * empty array is returned if no matching 326 * subscribed folders exist. 327 * @see #list 328 * @exception FolderNotFoundException if this folder does 329 * not exist. 330 * @exception MessagingException for other failures 331 */ listSubscribed(String pattern)332 public Folder[] listSubscribed(String pattern) throws MessagingException { 333 return list(pattern); 334 } 335 336 /** 337 * Convenience method that returns the list of folders under this 338 * Folder. This method just calls the <code>list(String pattern)</code> 339 * method with <code>"%"</code> as the match pattern. This method can 340 * be invoked on a closed Folder. 341 * 342 * @return array of Folder objects under this Folder. An 343 * empty array is returned if no subfolders exist. 344 * @see #list 345 * @exception FolderNotFoundException if this folder does 346 * not exist. 347 * @exception MessagingException for other failures 348 */ 349 list()350 public Folder[] list() throws MessagingException { 351 return list("%"); 352 } 353 354 /** 355 * Convenience method that returns the list of subscribed folders 356 * under this Folder. This method just calls the 357 * <code>listSubscribed(String pattern)</code> method with <code>"%"</code> 358 * as the match pattern. This method can be invoked on a closed Folder. 359 * 360 * @return array of subscribed Folder objects under this 361 * Folder. An empty array is returned if no subscribed 362 * subfolders exist. 363 * @see #listSubscribed 364 * @exception FolderNotFoundException if this folder does 365 * not exist. 366 * @exception MessagingException for other failures 367 */ listSubscribed()368 public Folder[] listSubscribed() throws MessagingException { 369 return listSubscribed("%"); 370 } 371 372 /** 373 * Return the delimiter character that separates this Folder's pathname 374 * from the names of immediate subfolders. This method can be invoked 375 * on a closed Folder. 376 * 377 * @exception FolderNotFoundException if the implementation 378 * requires the folder to exist, but it does not 379 * @return Hierarchy separator character 380 */ getSeparator()381 public abstract char getSeparator() throws MessagingException; 382 383 /** 384 * This folder can contain messages 385 */ 386 public final static int HOLDS_MESSAGES = 0x01; 387 388 /** 389 * This folder can contain other folders 390 */ 391 public final static int HOLDS_FOLDERS = 0x02; 392 393 /** 394 * Returns the type of this Folder, that is, whether this folder can hold 395 * messages or subfolders or both. The returned value is an integer 396 * bitfield with the appropriate bits set. This method can be invoked 397 * on a closed folder. 398 * 399 * @return integer with appropriate bits set 400 * @exception FolderNotFoundException if this folder does 401 * not exist. 402 * @see #HOLDS_FOLDERS 403 * @see #HOLDS_MESSAGES 404 */ getType()405 public abstract int getType() throws MessagingException; 406 407 /** 408 * Create this folder on the Store. When this folder is created, any 409 * folders in its path that do not exist are also created. <p> 410 * 411 * If the creation is successful, a CREATED FolderEvent is delivered 412 * to any FolderListeners registered on this Folder and this Store. 413 * 414 * @param type The type of this folder. 415 * 416 * @return true if the creation succeeds, else false. 417 * @exception MessagingException for failures 418 * @see #HOLDS_FOLDERS 419 * @see #HOLDS_MESSAGES 420 * @see javax.mail.event.FolderEvent 421 */ create(int type)422 public abstract boolean create(int type) throws MessagingException; 423 424 /** 425 * Returns true if this Folder is subscribed. <p> 426 * 427 * This method can be invoked on a closed Folder. <p> 428 * 429 * The default implementation provided here just returns true. 430 * 431 * @return true if this Folder is subscribed 432 */ isSubscribed()433 public boolean isSubscribed() { 434 return true; 435 } 436 437 /** 438 * Subscribe or unsubscribe this Folder. Not all Stores support 439 * subscription. <p> 440 * 441 * This method can be invoked on a closed Folder. <p> 442 * 443 * The implementation provided here just throws the 444 * MethodNotSupportedException. 445 * 446 * @param subscribe true to subscribe, false to unsubscribe 447 * @exception FolderNotFoundException if this folder does 448 * not exist. 449 * @exception MethodNotSupportedException if this store 450 * does not support subscription 451 * @exception MessagingException for other failures 452 */ setSubscribed(boolean subscribe)453 public void setSubscribed(boolean subscribe) 454 throws MessagingException { 455 throw new MethodNotSupportedException(); 456 } 457 458 /** 459 * Returns true if this Folder has new messages since the last time 460 * this indication was reset. When this indication is set or reset 461 * depends on the Folder implementation (and in the case of IMAP, 462 * depends on the server). This method can be used to implement 463 * a lightweight "check for new mail" operation on a Folder without 464 * opening it. (For example, a thread that monitors a mailbox and 465 * flags when it has new mail.) This method should indicate whether 466 * any messages in the Folder have the <code>RECENT</code> flag set. <p> 467 * 468 * Note that this is not an incremental check for new mail, i.e., 469 * it cannot be used to determine whether any new messages have 470 * arrived since the last time this method was invoked. To 471 * implement incremental checks, the Folder needs to be opened. <p> 472 * 473 * This method can be invoked on a closed Folder that can contain 474 * Messages. 475 * 476 * @return true if the Store has new Messages 477 * @exception FolderNotFoundException if this folder does 478 * not exist. 479 * @exception MessagingException for other failures 480 */ hasNewMessages()481 public abstract boolean hasNewMessages() throws MessagingException; 482 483 /** 484 * Return the Folder object corresponding to the given name. Note that 485 * this folder does not physically have to exist in the Store. The 486 * <code>exists()</code> method on a Folder indicates whether it really 487 * exists on the Store. <p> 488 * 489 * In some Stores, name can be an absolute path if it starts with the 490 * hierarchy delimiter. Otherwise, it is interpreted relative to 491 * this Folder. <p> 492 * 493 * Folder objects are not cached by the Store, so invoking this 494 * method on the same name multiple times will return that many 495 * distinct Folder objects. <p> 496 * 497 * This method can be invoked on a closed Folder. 498 * 499 * @param name name of the Folder 500 * @return Folder object 501 * @exception MessagingException for failures 502 */ getFolder(String name)503 public abstract Folder getFolder(String name) 504 throws MessagingException; 505 506 /** 507 * Delete this Folder. This method will succeed only on a closed 508 * Folder. <p> 509 * 510 * The <code>recurse</code> flag controls whether the deletion affects 511 * subfolders or not. If true, all subfolders are deleted, then this 512 * folder itself is deleted. If false, the behaviour is dependent on 513 * the folder type and is elaborated below: 514 * 515 * <ul> 516 * <li> 517 * The folder can contain only messages: (type == HOLDS_MESSAGES). 518 * <br> 519 * All messages within the folder are removed. The folder 520 * itself is then removed. An appropriate FolderEvent is generated by 521 * the Store and this folder. 522 * 523 * <li> 524 * The folder can contain only subfolders: (type == HOLDS_FOLDERS). 525 * <br> 526 * If this folder is empty (does not contain any 527 * subfolders at all), it is removed. An appropriate FolderEvent is 528 * generated by the Store and this folder.<br> 529 * If this folder contains any subfolders, the delete fails 530 * and returns false. 531 * 532 * <li> 533 * The folder can contain subfolders as well as messages: <br> 534 * If the folder is empty (no messages or subfolders), it 535 * is removed. If the folder contains no subfolders, but only messages, 536 * then all messages are removed. The folder itself is then removed. 537 * In both the above cases, an appropriate FolderEvent is 538 * generated by the Store and this folder. <p> 539 * 540 * If the folder contains subfolders there are 3 possible 541 * choices an implementation is free to do: 542 * 543 * <ol> 544 * <li> The operation fails, irrespective of whether this folder 545 * contains messages or not. Some implementations might elect to go 546 * with this simple approach. The delete() method returns false. 547 * 548 * <li> Any messages within the folder are removed. Subfolders 549 * are not removed. The folder itself is not removed or affected 550 * in any manner. The delete() method returns true. And the 551 * exists() method on this folder will return true indicating that 552 * this folder still exists. <br> 553 * An appropriate FolderEvent is generated by the Store and this folder. 554 * 555 * <li> Any messages within the folder are removed. Subfolders are 556 * not removed. The folder itself changes its type from 557 * HOLDS_FOLDERS | HOLDS_MESSAGES to HOLDS_FOLDERS. Thus new 558 * messages cannot be added to this folder, but new subfolders can 559 * be created underneath. The delete() method returns true indicating 560 * success. The exists() method on this folder will return true 561 * indicating that this folder still exists. <br> 562 * An appropriate FolderEvent is generated by the Store and this folder. 563 * </ol> 564 * </ul> 565 * 566 * @param recurse also delete subfolders? 567 * @return true if the Folder is deleted successfully 568 * @exception FolderNotFoundException if this folder does 569 * not exist 570 * @exception IllegalStateException if this folder is not in 571 * the closed state. 572 * @exception MessagingException for other failures 573 * @see javax.mail.event.FolderEvent 574 */ delete(boolean recurse)575 public abstract boolean delete(boolean recurse) 576 throws MessagingException; 577 578 /** 579 * Rename this Folder. This method will succeed only on a closed 580 * Folder. <p> 581 * 582 * If the rename is successful, a RENAMED FolderEvent is delivered 583 * to FolderListeners registered on this folder and its containing 584 * Store. 585 * 586 * @param f a folder representing the new name for this Folder 587 * @return true if the Folder is renamed successfully 588 * @exception FolderNotFoundException if this folder does 589 * not exist 590 * @exception IllegalStateException if this folder is not in 591 * the closed state. 592 * @exception MessagingException for other failures 593 * @see javax.mail.event.FolderEvent 594 */ renameTo(Folder f)595 public abstract boolean renameTo(Folder f) throws MessagingException; 596 597 /** 598 * The Folder is read only. The state and contents of this 599 * folder cannot be modified. 600 */ 601 public static final int READ_ONLY = 1; 602 603 /** 604 * The state and contents of this folder can be modified. 605 */ 606 public static final int READ_WRITE = 2; 607 608 /** 609 * Open this Folder. This method is valid only on Folders that 610 * can contain Messages and that are closed. <p> 611 * 612 * If this folder is opened successfully, an OPENED ConnectionEvent 613 * is delivered to any ConnectionListeners registered on this 614 * Folder. <p> 615 * 616 * The effect of opening multiple connections to the same folder 617 * on a specifc Store is implementation dependent. Some implementations 618 * allow multiple readers, but only one writer. Others allow 619 * multiple writers as well as readers. 620 * 621 * @param mode open the Folder READ_ONLY or READ_WRITE 622 * @exception FolderNotFoundException if this folder does 623 * not exist. 624 * @exception IllegalStateException if this folder is not in 625 * the closed state. 626 * @exception MessagingException for other failures 627 * @see #READ_ONLY 628 * @see #READ_WRITE 629 * @see #getType() 630 * @see javax.mail.event.ConnectionEvent 631 */ open(int mode)632 public abstract void open(int mode) throws MessagingException; 633 634 /** 635 * Close this Folder. This method is valid only on open Folders. <p> 636 * 637 * A CLOSED ConnectionEvent is delivered to any ConnectionListeners 638 * registered on this Folder. Note that the folder is closed even 639 * if this method terminates abnormally by throwing a 640 * MessagingException. 641 * 642 * @param expunge expunges all deleted messages if this flag is true 643 * @exception IllegalStateException if this folder is not opened 644 * @exception MessagingException for other failures 645 * @see javax.mail.event.ConnectionEvent 646 */ close(boolean expunge)647 public abstract void close(boolean expunge) throws MessagingException; 648 649 /** 650 * Close this Folder and expunge deleted messages. <p> 651 * 652 * A CLOSED ConnectionEvent is delivered to any ConnectionListeners 653 * registered on this Folder. Note that the folder is closed even 654 * if this method terminates abnormally by throwing a 655 * MessagingException. <p> 656 * 657 * This method supports the {@link java.lang.AutoCloseable AutoCloseable} 658 * interface. <p> 659 * 660 * This implementation calls <code>close(true)</code>. 661 * 662 * @exception IllegalStateException if this folder is not opened 663 * @exception MessagingException for other failures 664 * @see javax.mail.event.ConnectionEvent 665 * @since JavaMail 1.6 666 */ 667 @Override close()668 public void close() throws MessagingException { 669 close(true); 670 } 671 672 /** 673 * Indicates whether this Folder is in the 'open' state. 674 * @return true if this Folder is in the 'open' state. 675 */ isOpen()676 public abstract boolean isOpen(); 677 678 /** 679 * Return the open mode of this folder. Returns 680 * <code>Folder.READ_ONLY</code>, <code>Folder.READ_WRITE</code>, 681 * or -1 if the open mode is not known (usually only because an older 682 * <code>Folder</code> provider has not been updated to use this new 683 * method). 684 * 685 * @exception IllegalStateException if this folder is not opened 686 * @return the open mode of this folder 687 * @since JavaMail 1.1 688 */ getMode()689 public synchronized int getMode() { 690 if (!isOpen()) 691 throw new IllegalStateException("Folder not open"); 692 return mode; 693 } 694 695 /** 696 * Get the permanent flags supported by this Folder. Returns a Flags 697 * object that contains all the flags supported. <p> 698 * 699 * The special flag <code>Flags.Flag.USER </code> indicates that this Folder 700 * supports arbitrary user-defined flags. <p> 701 * 702 * The supported permanent flags for a folder may not be available 703 * until the folder is opened. 704 * 705 * @return permanent flags, or null if not known 706 */ getPermanentFlags()707 public abstract Flags getPermanentFlags(); 708 709 /** 710 * Get total number of messages in this Folder. <p> 711 * 712 * This method can be invoked on a closed folder. However, note 713 * that for some folder implementations, getting the total message 714 * count can be an expensive operation involving actually opening 715 * the folder. In such cases, a provider can choose not to support 716 * this functionality in the closed state, in which case this method 717 * must return -1. <p> 718 * 719 * Clients invoking this method on a closed folder must be aware 720 * that this is a potentially expensive operation. Clients must 721 * also be prepared to handle a return value of -1 in this case. 722 * 723 * @return total number of messages. -1 may be returned 724 * by certain implementations if this method is 725 * invoked on a closed folder. 726 * @exception FolderNotFoundException if this folder does 727 * not exist. 728 * @exception MessagingException for other failures 729 */ getMessageCount()730 public abstract int getMessageCount() throws MessagingException; 731 732 /** 733 * Get the number of new messages in this Folder. <p> 734 * 735 * This method can be invoked on a closed folder. However, note 736 * that for some folder implementations, getting the new message 737 * count can be an expensive operation involving actually opening 738 * the folder. In such cases, a provider can choose not to support 739 * this functionality in the closed state, in which case this method 740 * must return -1. <p> 741 * 742 * Clients invoking this method on a closed folder must be aware 743 * that this is a potentially expensive operation. Clients must 744 * also be prepared to handle a return value of -1 in this case. <p> 745 * 746 * This implementation returns -1 if this folder is closed. Else 747 * this implementation gets each Message in the folder using 748 * <code>getMessage(int)</code> and checks whether its 749 * <code>RECENT</code> flag is set. The total number of messages 750 * that have this flag set is returned. 751 * 752 * @return number of new messages. -1 may be returned 753 * by certain implementations if this method is 754 * invoked on a closed folder. 755 * @exception FolderNotFoundException if this folder does 756 * not exist. 757 * @exception MessagingException for other failures 758 */ getNewMessageCount()759 public synchronized int getNewMessageCount() 760 throws MessagingException { 761 if (!isOpen()) 762 return -1; 763 764 int newmsgs = 0; 765 int total = getMessageCount(); 766 for (int i = 1; i <= total; i++) { 767 try { 768 if (getMessage(i).isSet(Flags.Flag.RECENT)) 769 newmsgs++; 770 } catch (MessageRemovedException me) { 771 // This is an expunged message, ignore it. 772 continue; 773 } 774 } 775 return newmsgs; 776 } 777 778 /** 779 * Get the total number of unread messages in this Folder. <p> 780 * 781 * This method can be invoked on a closed folder. However, note 782 * that for some folder implementations, getting the unread message 783 * count can be an expensive operation involving actually opening 784 * the folder. In such cases, a provider can choose not to support 785 * this functionality in the closed state, in which case this method 786 * must return -1. <p> 787 * 788 * Clients invoking this method on a closed folder must be aware 789 * that this is a potentially expensive operation. Clients must 790 * also be prepared to handle a return value of -1 in this case. <p> 791 * 792 * This implementation returns -1 if this folder is closed. Else 793 * this implementation gets each Message in the folder using 794 * <code>getMessage(int)</code> and checks whether its 795 * <code>SEEN</code> flag is set. The total number of messages 796 * that do not have this flag set is returned. 797 * 798 * @return total number of unread messages. -1 may be returned 799 * by certain implementations if this method is 800 * invoked on a closed folder. 801 * @exception FolderNotFoundException if this folder does 802 * not exist. 803 * @exception MessagingException for other failures 804 */ getUnreadMessageCount()805 public synchronized int getUnreadMessageCount() 806 throws MessagingException { 807 if (!isOpen()) 808 return -1; 809 810 int unread = 0; 811 int total = getMessageCount(); 812 for (int i = 1; i <= total; i++) { 813 try { 814 if (!getMessage(i).isSet(Flags.Flag.SEEN)) 815 unread++; 816 } catch (MessageRemovedException me) { 817 // This is an expunged message, ignore it. 818 continue; 819 } 820 } 821 return unread; 822 } 823 824 /** 825 * Get the number of deleted messages in this Folder. <p> 826 * 827 * This method can be invoked on a closed folder. However, note 828 * that for some folder implementations, getting the deleted message 829 * count can be an expensive operation involving actually opening 830 * the folder. In such cases, a provider can choose not to support 831 * this functionality in the closed state, in which case this method 832 * must return -1. <p> 833 * 834 * Clients invoking this method on a closed folder must be aware 835 * that this is a potentially expensive operation. Clients must 836 * also be prepared to handle a return value of -1 in this case. <p> 837 * 838 * This implementation returns -1 if this folder is closed. Else 839 * this implementation gets each Message in the folder using 840 * <code>getMessage(int)</code> and checks whether its 841 * <code>DELETED</code> flag is set. The total number of messages 842 * that have this flag set is returned. 843 * 844 * @return number of deleted messages. -1 may be returned 845 * by certain implementations if this method is 846 * invoked on a closed folder. 847 * @exception FolderNotFoundException if this folder does 848 * not exist. 849 * @exception MessagingException for other failures 850 * @since JavaMail 1.3 851 */ getDeletedMessageCount()852 public synchronized int getDeletedMessageCount() throws MessagingException { 853 if (!isOpen()) 854 return -1; 855 856 int deleted = 0; 857 int total = getMessageCount(); 858 for (int i = 1; i <= total; i++) { 859 try { 860 if (getMessage(i).isSet(Flags.Flag.DELETED)) 861 deleted++; 862 } catch (MessageRemovedException me) { 863 // This is an expunged message, ignore it. 864 continue; 865 } 866 } 867 return deleted; 868 } 869 870 /** 871 * Get the Message object corresponding to the given message 872 * number. A Message object's message number is the relative 873 * position of this Message in its Folder. Messages are numbered 874 * starting at 1 through the total number of message in the folder. 875 * Note that the message number for a particular Message can change 876 * during a session if other messages in the Folder are deleted and 877 * the Folder is expunged. <p> 878 * 879 * Message objects are light-weight references to the actual message 880 * that get filled up on demand. Hence Folder implementations are 881 * expected to provide light-weight Message objects. <p> 882 * 883 * Unlike Folder objects, repeated calls to getMessage with the 884 * same message number will return the same Message object, as 885 * long as no messages in this folder have been expunged. <p> 886 * 887 * Since message numbers can change within a session if the folder 888 * is expunged , clients are advised not to use message numbers as 889 * references to messages. Use Message objects instead. 890 * 891 * @param msgnum the message number 892 * @return the Message object 893 * @see #getMessageCount 894 * @see #fetch 895 * @exception FolderNotFoundException if this folder does 896 * not exist. 897 * @exception IllegalStateException if this folder is not opened 898 * @exception IndexOutOfBoundsException if the message number 899 * is out of range. 900 * @exception MessagingException for other failures 901 */ getMessage(int msgnum)902 public abstract Message getMessage(int msgnum) 903 throws MessagingException; 904 905 /** 906 * Get the Message objects for message numbers ranging from start 907 * through end, both start and end inclusive. Note that message 908 * numbers start at 1, not 0. <p> 909 * 910 * Message objects are light-weight references to the actual message 911 * that get filled up on demand. Hence Folder implementations are 912 * expected to provide light-weight Message objects. <p> 913 * 914 * This implementation uses getMessage(index) to obtain the required 915 * Message objects. Note that the returned array must contain 916 * <code>(end-start+1)</code> Message objects. 917 * 918 * @param start the number of the first message 919 * @param end the number of the last message 920 * @return the Message objects 921 * @see #fetch 922 * @exception FolderNotFoundException if this folder does 923 * not exist. 924 * @exception IllegalStateException if this folder is not opened. 925 * @exception IndexOutOfBoundsException if the start or end 926 * message numbers are out of range. 927 * @exception MessagingException for other failures 928 */ getMessages(int start, int end)929 public synchronized Message[] getMessages(int start, int end) 930 throws MessagingException { 931 Message[] msgs = new Message[end - start +1]; 932 for (int i = start; i <= end; i++) 933 msgs[i - start] = getMessage(i); 934 return msgs; 935 } 936 937 /** 938 * Get the Message objects for message numbers specified in 939 * the array. <p> 940 * 941 * Message objects are light-weight references to the actual message 942 * that get filled up on demand. Hence Folder implementations are 943 * expected to provide light-weight Message objects. <p> 944 * 945 * This implementation uses getMessage(index) to obtain the required 946 * Message objects. Note that the returned array must contain 947 * <code>msgnums.length</code> Message objects 948 * 949 * @param msgnums the array of message numbers 950 * @return the array of Message objects. 951 * @see #fetch 952 * @exception FolderNotFoundException if this folder does 953 * not exist. 954 * @exception IllegalStateException if this folder is not opened. 955 * @exception IndexOutOfBoundsException if any message number 956 * in the given array is out of range. 957 * @exception MessagingException for other failures 958 */ getMessages(int[] msgnums)959 public synchronized Message[] getMessages(int[] msgnums) 960 throws MessagingException { 961 int len = msgnums.length; 962 Message[] msgs = new Message[len]; 963 for (int i = 0; i < len; i++) 964 msgs[i] = getMessage(msgnums[i]); 965 return msgs; 966 } 967 968 /** 969 * Get all Message objects from this Folder. Returns an empty array 970 * if the folder is empty. 971 * 972 * Clients can use Message objects (instead of sequence numbers) 973 * as references to the messages within a folder; this method supplies 974 * the Message objects to the client. Folder implementations are 975 * expected to provide light-weight Message objects, which get 976 * filled on demand. <p> 977 * 978 * This implementation invokes <code>getMessageCount()</code> to get 979 * the current message count and then uses <code>getMessage()</code> 980 * to get Message objects from 1 till the message count. 981 * 982 * @return array of Message objects, empty array if folder 983 * is empty. 984 * @see #fetch 985 * @exception FolderNotFoundException if this folder does 986 * not exist. 987 * @exception IllegalStateException if this folder is not opened. 988 * @exception MessagingException for other failures 989 */ getMessages()990 public synchronized Message[] getMessages() throws MessagingException { 991 if (!isOpen()) // otherwise getMessageCount might return -1 992 throw new IllegalStateException("Folder not open"); 993 int total = getMessageCount(); 994 Message[] msgs = new Message[total]; 995 for (int i = 1; i <= total; i++) 996 msgs[i-1] = getMessage(i); 997 return msgs; 998 } 999 1000 /** 1001 * Append given Messages to this folder. This method can be 1002 * invoked on a closed Folder. An appropriate MessageCountEvent 1003 * is delivered to any MessageCountListener registered on this 1004 * folder when the messages arrive in the folder. <p> 1005 * 1006 * Folder implementations must not abort this operation if a 1007 * Message in the given message array turns out to be an 1008 * expunged Message. 1009 * 1010 * @param msgs array of Messages to be appended 1011 * @exception FolderNotFoundException if this folder does 1012 * not exist. 1013 * @exception MessagingException if the append failed. 1014 */ appendMessages(Message[] msgs)1015 public abstract void appendMessages(Message[] msgs) 1016 throws MessagingException; 1017 1018 /** 1019 * Prefetch the items specified in the FetchProfile for the 1020 * given Messages. <p> 1021 * 1022 * Clients use this method to indicate that the specified items are 1023 * needed en-masse for the given message range. Implementations are 1024 * expected to retrieve these items for the given message range in 1025 * a efficient manner. Note that this method is just a hint to the 1026 * implementation to prefetch the desired items. <p> 1027 * 1028 * An example is a client filling its header-view window with 1029 * the Subject, From and X-mailer headers for all messages in the 1030 * folder. 1031 * <blockquote><pre> 1032 * 1033 * Message[] msgs = folder.getMessages(); 1034 * 1035 * FetchProfile fp = new FetchProfile(); 1036 * fp.add(FetchProfile.Item.ENVELOPE); 1037 * fp.add("X-mailer"); 1038 * folder.fetch(msgs, fp); 1039 * 1040 * for (int i = 0; i < folder.getMessageCount(); i++) { 1041 * display(msg[i].getFrom()); 1042 * display(msg[i].getSubject()); 1043 * display(msg[i].getHeader("X-mailer")); 1044 * } 1045 * 1046 * </pre></blockquote><p> 1047 * 1048 * The implementation provided here just returns without 1049 * doing anything useful. Providers wanting to provide a real 1050 * implementation for this method should override this method. 1051 * 1052 * @param msgs fetch items for these messages 1053 * @param fp the FetchProfile 1054 * @exception IllegalStateException if this folder is not opened 1055 * @exception MessagingException for other failures 1056 */ fetch(Message[] msgs, FetchProfile fp)1057 public void fetch(Message[] msgs, FetchProfile fp) 1058 throws MessagingException { 1059 return; 1060 } 1061 1062 /** 1063 * Set the specified flags on the messages specified in the array. 1064 * This will result in appropriate MessageChangedEvents being 1065 * delivered to any MessageChangedListener registered on this 1066 * Message's containing folder. <p> 1067 * 1068 * Note that the specified Message objects <strong>must</strong> 1069 * belong to this folder. Certain Folder implementations can 1070 * optimize the operation of setting Flags for a group of messages, 1071 * so clients might want to use this method, rather than invoking 1072 * <code>Message.setFlags</code> for each Message. <p> 1073 * 1074 * This implementation degenerates to invoking <code>setFlags()</code> 1075 * on each Message object. Specific Folder implementations that can 1076 * optimize this case should do so. 1077 * Also, an implementation must not abort the operation if a Message 1078 * in the array turns out to be an expunged Message. 1079 * 1080 * @param msgs the array of message objects 1081 * @param flag Flags object containing the flags to be set 1082 * @param value set the flags to this boolean value 1083 * @exception IllegalStateException if this folder is not opened 1084 * or if it has been opened READ_ONLY. 1085 * @exception MessagingException for other failures 1086 * @see Message#setFlags 1087 * @see javax.mail.event.MessageChangedEvent 1088 */ setFlags(Message[] msgs, Flags flag, boolean value)1089 public synchronized void setFlags(Message[] msgs, 1090 Flags flag, boolean value) throws MessagingException { 1091 for (int i = 0; i < msgs.length; i++) { 1092 try { 1093 msgs[i].setFlags(flag, value); 1094 } catch (MessageRemovedException me) { 1095 // This message is expunged, skip 1096 } 1097 } 1098 } 1099 1100 /** 1101 * Set the specified flags on the messages numbered from start 1102 * through end, both start and end inclusive. Note that message 1103 * numbers start at 1, not 0. 1104 * This will result in appropriate MessageChangedEvents being 1105 * delivered to any MessageChangedListener registered on this 1106 * Message's containing folder. <p> 1107 * 1108 * Certain Folder implementations can 1109 * optimize the operation of setting Flags for a group of messages, 1110 * so clients might want to use this method, rather than invoking 1111 * <code>Message.setFlags</code> for each Message. <p> 1112 * 1113 * The default implementation uses <code>getMessage(int)</code> to 1114 * get each <code>Message</code> object and then invokes 1115 * <code>setFlags</code> on that object to set the flags. 1116 * Specific Folder implementations that can optimize this case should do so. 1117 * Also, an implementation must not abort the operation if a message 1118 * number refers to an expunged message. 1119 * 1120 * @param start the number of the first message 1121 * @param end the number of the last message 1122 * @param flag Flags object containing the flags to be set 1123 * @param value set the flags to this boolean value 1124 * @exception IllegalStateException if this folder is not opened 1125 * or if it has been opened READ_ONLY. 1126 * @exception IndexOutOfBoundsException if the start or end 1127 * message numbers are out of range. 1128 * @exception MessagingException for other failures 1129 * @see Message#setFlags 1130 * @see javax.mail.event.MessageChangedEvent 1131 */ setFlags(int start, int end, Flags flag, boolean value)1132 public synchronized void setFlags(int start, int end, 1133 Flags flag, boolean value) throws MessagingException { 1134 for (int i = start; i <= end; i++) { 1135 try { 1136 Message msg = getMessage(i); 1137 msg.setFlags(flag, value); 1138 } catch (MessageRemovedException me) { 1139 // This message is expunged, skip 1140 } 1141 } 1142 } 1143 1144 /** 1145 * Set the specified flags on the messages whose message numbers 1146 * are in the array. 1147 * This will result in appropriate MessageChangedEvents being 1148 * delivered to any MessageChangedListener registered on this 1149 * Message's containing folder. <p> 1150 * 1151 * Certain Folder implementations can 1152 * optimize the operation of setting Flags for a group of messages, 1153 * so clients might want to use this method, rather than invoking 1154 * <code>Message.setFlags</code> for each Message. <p> 1155 * 1156 * The default implementation uses <code>getMessage(int)</code> to 1157 * get each <code>Message</code> object and then invokes 1158 * <code>setFlags</code> on that object to set the flags. 1159 * Specific Folder implementations that can optimize this case should do so. 1160 * Also, an implementation must not abort the operation if a message 1161 * number refers to an expunged message. 1162 * 1163 * @param msgnums the array of message numbers 1164 * @param flag Flags object containing the flags to be set 1165 * @param value set the flags to this boolean value 1166 * @exception IllegalStateException if this folder is not opened 1167 * or if it has been opened READ_ONLY. 1168 * @exception IndexOutOfBoundsException if any message number 1169 * in the given array is out of range. 1170 * @exception MessagingException for other failures 1171 * @see Message#setFlags 1172 * @see javax.mail.event.MessageChangedEvent 1173 */ setFlags(int[] msgnums, Flags flag, boolean value)1174 public synchronized void setFlags(int[] msgnums, 1175 Flags flag, boolean value) throws MessagingException { 1176 for (int i = 0; i < msgnums.length; i++) { 1177 try { 1178 Message msg = getMessage(msgnums[i]); 1179 msg.setFlags(flag, value); 1180 } catch (MessageRemovedException me) { 1181 // This message is expunged, skip 1182 } 1183 } 1184 } 1185 1186 /** 1187 * Copy the specified Messages from this Folder into another 1188 * Folder. This operation appends these Messages to the 1189 * destination Folder. The destination Folder does not have to 1190 * be opened. An appropriate MessageCountEvent 1191 * is delivered to any MessageCountListener registered on the 1192 * destination folder when the messages arrive in the folder. <p> 1193 * 1194 * Note that the specified Message objects <strong>must</strong> 1195 * belong to this folder. Folder implementations might be able 1196 * to optimize this method by doing server-side copies. <p> 1197 * 1198 * This implementation just invokes <code>appendMessages()</code> 1199 * on the destination folder to append the given Messages. Specific 1200 * folder implementations that support server-side copies should 1201 * do so, if the destination folder's Store is the same as this 1202 * folder's Store. 1203 * Also, an implementation must not abort the operation if a 1204 * Message in the array turns out to be an expunged Message. 1205 * 1206 * @param msgs the array of message objects 1207 * @param folder the folder to copy the messages to 1208 * @exception FolderNotFoundException if the destination 1209 * folder does not exist. 1210 * @exception IllegalStateException if this folder is not opened. 1211 * @exception MessagingException for other failures 1212 * @see #appendMessages 1213 */ copyMessages(Message[] msgs, Folder folder)1214 public void copyMessages(Message[] msgs, Folder folder) 1215 throws MessagingException { 1216 if (!folder.exists()) 1217 throw new FolderNotFoundException( 1218 folder.getFullName() + " does not exist", 1219 folder); 1220 1221 folder.appendMessages(msgs); 1222 } 1223 1224 /** 1225 * Expunge (permanently remove) messages marked DELETED. Returns an 1226 * array containing the expunged message objects. The 1227 * <code>getMessageNumber</code> method 1228 * on each of these message objects returns that Message's original 1229 * (that is, prior to the expunge) sequence number. A MessageCountEvent 1230 * containing the expunged messages is delivered to any 1231 * MessageCountListeners registered on the folder. <p> 1232 * 1233 * Expunge causes the renumbering of Message objects subsequent to 1234 * the expunged messages. Clients that use message numbers as 1235 * references to messages should be aware of this and should be 1236 * prepared to deal with the situation (probably by flushing out 1237 * existing message number caches and reloading them). Because of 1238 * this complexity, it is better for clients to use Message objects 1239 * as references to messages, rather than message numbers. Any 1240 * expunged Messages objects still have to be pruned, but other 1241 * Messages in that folder are not affected by the expunge. <p> 1242 * 1243 * After a message is expunged, only the <code>isExpunged</code> and 1244 * <code>getMessageNumber</code> methods are still valid on the 1245 * corresponding Message object; other methods may throw 1246 * <code>MessageRemovedException</code> 1247 * 1248 * @return array of expunged Message objects 1249 * @exception FolderNotFoundException if this folder does not 1250 * exist 1251 * @exception IllegalStateException if this folder is not opened. 1252 * @exception MessagingException for other failures 1253 * @see Message#isExpunged 1254 * @see javax.mail.event.MessageCountEvent 1255 */ expunge()1256 public abstract Message[] expunge() throws MessagingException; 1257 1258 /** 1259 * Search this Folder for messages matching the specified 1260 * search criterion. Returns an array containing the matching 1261 * messages . Returns an empty array if no matches were found. <p> 1262 * 1263 * This implementation invokes 1264 * <code>search(term, getMessages())</code>, to apply the search 1265 * over all the messages in this folder. Providers that can implement 1266 * server-side searching might want to override this method to provide 1267 * a more efficient implementation. 1268 * 1269 * @param term the search criterion 1270 * @return array of matching messages 1271 * @exception javax.mail.search.SearchException if the search 1272 * term is too complex for the implementation to handle. 1273 * @exception FolderNotFoundException if this folder does 1274 * not exist. 1275 * @exception IllegalStateException if this folder is not opened. 1276 * @exception MessagingException for other failures 1277 * @see javax.mail.search.SearchTerm 1278 */ search(SearchTerm term)1279 public Message[] search(SearchTerm term) throws MessagingException { 1280 return search(term, getMessages()); 1281 } 1282 1283 /** 1284 * Search the given array of messages for those that match the 1285 * specified search criterion. Returns an array containing the 1286 * matching messages. Returns an empty array if no matches were 1287 * found. <p> 1288 * 1289 * Note that the specified Message objects <strong>must</strong> 1290 * belong to this folder. <p> 1291 * 1292 * This implementation iterates through the given array of messages, 1293 * and applies the search criterion on each message by calling 1294 * its <code>match()</code> method with the given term. The 1295 * messages that succeed in the match are returned. Providers 1296 * that can implement server-side searching might want to override 1297 * this method to provide a more efficient implementation. If the 1298 * search term is too complex or contains user-defined terms that 1299 * cannot be executed on the server, providers may elect to either 1300 * throw a SearchException or degenerate to client-side searching by 1301 * calling <code>super.search()</code> to invoke this implementation. 1302 * 1303 * @param term the search criterion 1304 * @param msgs the messages to be searched 1305 * @return array of matching messages 1306 * @exception javax.mail.search.SearchException if the search 1307 * term is too complex for the implementation to handle. 1308 * @exception IllegalStateException if this folder is not opened 1309 * @exception MessagingException for other failures 1310 * @see javax.mail.search.SearchTerm 1311 */ search(SearchTerm term, Message[] msgs)1312 public Message[] search(SearchTerm term, Message[] msgs) 1313 throws MessagingException { 1314 List<Message> matchedMsgs = new ArrayList<>(); 1315 1316 // Run thru the given messages 1317 for (Message msg : msgs) { 1318 try { 1319 if (msg.match(term)) // matched 1320 matchedMsgs.add(msg); // add it 1321 } catch(MessageRemovedException mrex) { } 1322 } 1323 1324 return matchedMsgs.toArray(new Message[matchedMsgs.size()]); 1325 } 1326 1327 /* 1328 * The set of listeners are stored in Vectors appropriate to their 1329 * type. We mark all listener Vectors as "volatile" because, while 1330 * we initialize them inside this folder's synchronization lock, 1331 * they are accessed (checked for null) in the "notify" methods, 1332 * which can't be synchronized due to lock ordering constraints. 1333 * Since the listener fields (the handles on the Vector objects) 1334 * are only ever set, and are never cleared, we believe this is 1335 * safe. The code that dispatches the notifications will either 1336 * see the null and assume there are no listeners or will see the 1337 * Vector and will process the listeners. There's an inherent race 1338 * between adding a listener and notifying the listeners; the lack 1339 * of synchronization during notification does not make the race 1340 * condition significantly worse. If one thread is setting a 1341 * listener at the "same" time an event is being dispatched, the 1342 * dispatch code might not see the listener right away. The 1343 * dispatch code doesn't have to worry about the Vector handle 1344 * being set to null, and thus using an out-of-date set of 1345 * listeners, because we never set the field to null. 1346 */ 1347 1348 // Vector of connection listeners. 1349 private volatile Vector<ConnectionListener> connectionListeners = null; 1350 1351 /** 1352 * Add a listener for Connection events on this Folder. <p> 1353 * 1354 * The implementation provided here adds this listener 1355 * to an internal list of ConnectionListeners. 1356 * 1357 * @param l the Listener for Connection events 1358 * @see javax.mail.event.ConnectionEvent 1359 */ 1360 public synchronized void addConnectionListener(ConnectionListener l)1361 addConnectionListener(ConnectionListener l) { 1362 if (connectionListeners == null) 1363 connectionListeners = new Vector<>(); 1364 connectionListeners.addElement(l); 1365 } 1366 1367 /** 1368 * Remove a Connection event listener. <p> 1369 * 1370 * The implementation provided here removes this listener 1371 * from the internal list of ConnectionListeners. 1372 * 1373 * @param l the listener 1374 * @see #addConnectionListener 1375 */ 1376 public synchronized void removeConnectionListener(ConnectionListener l)1377 removeConnectionListener(ConnectionListener l) { 1378 if (connectionListeners != null) 1379 connectionListeners.removeElement(l); 1380 } 1381 1382 /** 1383 * Notify all ConnectionListeners. Folder implementations are 1384 * expected to use this method to broadcast connection events. <p> 1385 * 1386 * The provided implementation queues the event into 1387 * an internal event queue. An event dispatcher thread dequeues 1388 * events from the queue and dispatches them to the registered 1389 * ConnectionListeners. Note that the event dispatching occurs 1390 * in a separate thread, thus avoiding potential deadlock problems. 1391 * 1392 * @param type the ConnectionEvent type 1393 * @see javax.mail.event.ConnectionEvent 1394 */ notifyConnectionListeners(int type)1395 protected void notifyConnectionListeners(int type) { 1396 if (connectionListeners != null) { 1397 ConnectionEvent e = new ConnectionEvent(this, type); 1398 queueEvent(e, connectionListeners); 1399 } 1400 1401 /* Fix for broken JDK1.1.x Garbage collector : 1402 * The 'conservative' GC in JDK1.1.x occasionally fails to 1403 * garbage-collect Threads which are in the wait state. 1404 * This would result in thread (and consequently memory) leaks. 1405 * 1406 * We attempt to fix this by sending a 'terminator' event 1407 * to the queue, after we've sent the CLOSED event. The 1408 * terminator event causes the event-dispatching thread to 1409 * self destruct. 1410 */ 1411 if (type == ConnectionEvent.CLOSED) 1412 q.terminateQueue(); 1413 } 1414 1415 // Vector of folder listeners 1416 private volatile Vector<FolderListener> folderListeners = null; 1417 1418 /** 1419 * Add a listener for Folder events on this Folder. <p> 1420 * 1421 * The implementation provided here adds this listener 1422 * to an internal list of FolderListeners. 1423 * 1424 * @param l the Listener for Folder events 1425 * @see javax.mail.event.FolderEvent 1426 */ addFolderListener(FolderListener l)1427 public synchronized void addFolderListener(FolderListener l) { 1428 if (folderListeners == null) 1429 folderListeners = new Vector<>(); 1430 folderListeners.addElement(l); 1431 } 1432 1433 /** 1434 * Remove a Folder event listener. <p> 1435 * 1436 * The implementation provided here removes this listener 1437 * from the internal list of FolderListeners. 1438 * 1439 * @param l the listener 1440 * @see #addFolderListener 1441 */ removeFolderListener(FolderListener l)1442 public synchronized void removeFolderListener(FolderListener l) { 1443 if (folderListeners != null) 1444 folderListeners.removeElement(l); 1445 } 1446 1447 /** 1448 * Notify all FolderListeners registered on this Folder and 1449 * this folder's Store. Folder implementations are expected 1450 * to use this method to broadcast Folder events. <p> 1451 * 1452 * The implementation provided here queues the event into 1453 * an internal event queue. An event dispatcher thread dequeues 1454 * events from the queue and dispatches them to the 1455 * FolderListeners registered on this folder. The implementation 1456 * also invokes <code>notifyFolderListeners</code> on this folder's 1457 * Store to notify any FolderListeners registered on the store. 1458 * 1459 * @param type type of FolderEvent 1460 * @see #notifyFolderRenamedListeners 1461 */ notifyFolderListeners(int type)1462 protected void notifyFolderListeners(int type) { 1463 if (folderListeners != null) { 1464 FolderEvent e = new FolderEvent(this, this, type); 1465 queueEvent(e, folderListeners); 1466 } 1467 store.notifyFolderListeners(type, this); 1468 } 1469 1470 /** 1471 * Notify all FolderListeners registered on this Folder and 1472 * this folder's Store about the renaming of this folder. 1473 * Folder implementations are expected to use this method to 1474 * broadcast Folder events indicating the renaming of folders. <p> 1475 * 1476 * The implementation provided here queues the event into 1477 * an internal event queue. An event dispatcher thread dequeues 1478 * events from the queue and dispatches them to the 1479 * FolderListeners registered on this folder. The implementation 1480 * also invokes <code>notifyFolderRenamedListeners</code> on this 1481 * folder's Store to notify any FolderListeners registered on the store. 1482 * 1483 * @param folder Folder representing the new name. 1484 * @see #notifyFolderListeners 1485 * @since JavaMail 1.1 1486 */ notifyFolderRenamedListeners(Folder folder)1487 protected void notifyFolderRenamedListeners(Folder folder) { 1488 if (folderListeners != null) { 1489 FolderEvent e = new FolderEvent(this, this, folder, 1490 FolderEvent.RENAMED); 1491 queueEvent(e, folderListeners); 1492 } 1493 store.notifyFolderRenamedListeners(this, folder); 1494 } 1495 1496 // Vector of MessageCount listeners 1497 private volatile Vector<MessageCountListener> messageCountListeners = null; 1498 1499 /** 1500 * Add a listener for MessageCount events on this Folder. <p> 1501 * 1502 * The implementation provided here adds this listener 1503 * to an internal list of MessageCountListeners. 1504 * 1505 * @param l the Listener for MessageCount events 1506 * @see javax.mail.event.MessageCountEvent 1507 */ addMessageCountListener(MessageCountListener l)1508 public synchronized void addMessageCountListener(MessageCountListener l) { 1509 if (messageCountListeners == null) 1510 messageCountListeners = new Vector<>(); 1511 messageCountListeners.addElement(l); 1512 } 1513 1514 /** 1515 * Remove a MessageCount listener. <p> 1516 * 1517 * The implementation provided here removes this listener 1518 * from the internal list of MessageCountListeners. 1519 * 1520 * @param l the listener 1521 * @see #addMessageCountListener 1522 */ 1523 public synchronized void removeMessageCountListener(MessageCountListener l)1524 removeMessageCountListener(MessageCountListener l) { 1525 if (messageCountListeners != null) 1526 messageCountListeners.removeElement(l); 1527 } 1528 1529 /** 1530 * Notify all MessageCountListeners about the addition of messages 1531 * into this folder. Folder implementations are expected to use this 1532 * method to broadcast MessageCount events for indicating arrival of 1533 * new messages. <p> 1534 * 1535 * The provided implementation queues the event into 1536 * an internal event queue. An event dispatcher thread dequeues 1537 * events from the queue and dispatches them to the registered 1538 * MessageCountListeners. Note that the event dispatching occurs 1539 * in a separate thread, thus avoiding potential deadlock problems. 1540 * 1541 * @param msgs the messages that were added 1542 */ notifyMessageAddedListeners(Message[] msgs)1543 protected void notifyMessageAddedListeners(Message[] msgs) { 1544 if (messageCountListeners == null) 1545 return; 1546 1547 MessageCountEvent e = new MessageCountEvent( 1548 this, 1549 MessageCountEvent.ADDED, 1550 false, 1551 msgs); 1552 1553 queueEvent(e, messageCountListeners); 1554 } 1555 1556 /** 1557 * Notify all MessageCountListeners about the removal of messages 1558 * from this Folder. Folder implementations are expected to use this 1559 * method to broadcast MessageCount events indicating removal of 1560 * messages. <p> 1561 * 1562 * The provided implementation queues the event into 1563 * an internal event queue. An event dispatcher thread dequeues 1564 * events from the queue and dispatches them to the registered 1565 * MessageCountListeners. Note that the event dispatching occurs 1566 * in a separate thread, thus avoiding potential deadlock problems. 1567 * 1568 * @param removed was the message removed by this client? 1569 * @param msgs the messages that were removed 1570 */ notifyMessageRemovedListeners(boolean removed, Message[] msgs)1571 protected void notifyMessageRemovedListeners(boolean removed, 1572 Message[] msgs) { 1573 if (messageCountListeners == null) 1574 return; 1575 1576 MessageCountEvent e = new MessageCountEvent( 1577 this, 1578 MessageCountEvent.REMOVED, 1579 removed, 1580 msgs); 1581 queueEvent(e, messageCountListeners); 1582 } 1583 1584 // Vector of MessageChanged listeners. 1585 private volatile Vector<MessageChangedListener> messageChangedListeners 1586 = null; 1587 1588 /** 1589 * Add a listener for MessageChanged events on this Folder. <p> 1590 * 1591 * The implementation provided here adds this listener 1592 * to an internal list of MessageChangedListeners. 1593 * 1594 * @param l the Listener for MessageChanged events 1595 * @see javax.mail.event.MessageChangedEvent 1596 */ 1597 public synchronized void addMessageChangedListener(MessageChangedListener l)1598 addMessageChangedListener(MessageChangedListener l) { 1599 if (messageChangedListeners == null) 1600 messageChangedListeners = new Vector<>(); 1601 messageChangedListeners.addElement(l); 1602 } 1603 1604 /** 1605 * Remove a MessageChanged listener. <p> 1606 * 1607 * The implementation provided here removes this listener 1608 * from the internal list of MessageChangedListeners. 1609 * 1610 * @param l the listener 1611 * @see #addMessageChangedListener 1612 */ 1613 public synchronized void removeMessageChangedListener(MessageChangedListener l)1614 removeMessageChangedListener(MessageChangedListener l) { 1615 if (messageChangedListeners != null) 1616 messageChangedListeners.removeElement(l); 1617 } 1618 1619 /** 1620 * Notify all MessageChangedListeners. Folder implementations are 1621 * expected to use this method to broadcast MessageChanged events. <p> 1622 * 1623 * The provided implementation queues the event into 1624 * an internal event queue. An event dispatcher thread dequeues 1625 * events from the queue and dispatches them to registered 1626 * MessageChangedListeners. Note that the event dispatching occurs 1627 * in a separate thread, thus avoiding potential deadlock problems. 1628 * 1629 * @param type the MessageChangedEvent type 1630 * @param msg the message that changed 1631 */ notifyMessageChangedListeners(int type, Message msg)1632 protected void notifyMessageChangedListeners(int type, Message msg) { 1633 if (messageChangedListeners == null) 1634 return; 1635 1636 MessageChangedEvent e = new MessageChangedEvent(this, type, msg); 1637 queueEvent(e, messageChangedListeners); 1638 } 1639 1640 /* 1641 * Add the event and vector of listeners to the queue to be delivered. 1642 */ 1643 @SuppressWarnings("unchecked") queueEvent(MailEvent event, Vector<? extends EventListener> vector)1644 private void queueEvent(MailEvent event, 1645 Vector<? extends EventListener> vector) { 1646 /* 1647 * Copy the vector in order to freeze the state of the set 1648 * of EventListeners the event should be delivered to prior 1649 * to delivery. This ensures that any changes made to the 1650 * Vector from a target listener's method during the delivery 1651 * of this event will not take effect until after the event is 1652 * delivered. 1653 */ 1654 Vector<? extends EventListener> v = (Vector)vector.clone(); 1655 q.enqueue(event, v); 1656 } 1657 1658 @Override finalize()1659 protected void finalize() throws Throwable { 1660 try { 1661 q.terminateQueue(); 1662 } finally { 1663 super.finalize(); 1664 } 1665 } 1666 1667 /** 1668 * override the default toString(), it will return the String 1669 * from Folder.getFullName() or if that is null, it will use 1670 * the default toString() behavior. 1671 */ 1672 1673 @Override toString()1674 public String toString() { 1675 String s = getFullName(); 1676 if (s != null) 1677 return s; 1678 else 1679 return super.toString(); 1680 } 1681 } 1682