Package net.suberic.pooka.gui

Source Code of net.suberic.pooka.gui.NewMessageProxy

package net.suberic.pooka.gui;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.Hashtable;
import java.util.Vector;

import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.internet.InternetHeaders;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.SwingUtilities;

import net.suberic.pooka.Attachment;
import net.suberic.pooka.FolderInfo;
import net.suberic.pooka.NewMessageInfo;
import net.suberic.pooka.OperationCancelledException;
import net.suberic.pooka.OutgoingMailServer;
import net.suberic.pooka.Pooka;
import net.suberic.pooka.UserProfile;
import net.suberic.pooka.gui.crypto.CryptoStatusDisplay;
//import net.suberic.pooka.gui.crypto.NewMessageCryptoDisplay;

/**
* This class represents a new message that is being written.
*/
public class NewMessageProxy extends MessageProxy {
  Hashtable commands;

  NewMessageCryptoInfo mCryptoInfo = null;

  private static Vector allUnsentProxies = new Vector();

  boolean sendLock = false;

  public NewMessageProxy(NewMessageInfo newMessage) {
    messageInfo = newMessage;
    messageInfo.setMessageProxy(this);

    mCryptoInfo = new NewMessageCryptoInfo(newMessage);

    commands = new Hashtable();

    Action[] actions = getActions();
    if (actions != null) {
      for (int i = 0; i < actions.length; i++) {
        Action a = actions[i];
        commands.put(a.getValue(Action.NAME), a);
      }
    }

    allUnsentProxies.add(this);

  }

  public void openWindow() {
    // shouldn't have to open window.
  }

  public void moveMessage(Folder targetFolder) {
    // shouldn't have to.  might want to implement this to move a message
    // to drafts, though.
  }

  /**
   * This sends the Message associated with this MessageProxy.
   *
   * If this MessageProxy has a MessageUI associated with it, it
   * will try to load the information from it, and then send the message.
   * Otherwise, it will just try sending the message as-is.
   *
   * If the Message.sendImmediately property is set, then the method will
   * also pop up and error window if there are any problems sending the
   * queued messages.
   *
   * If there is a MessageUI associated with this Proxy, and either
   * there are no errors sending the message, or the Message is just added
   * to the Queue and not sent yet, the Window will also be closed.
   *
   */
  public void send() {
    // thread:  AwtEvent

    synchronized(this) {
      if (sendLock) {
        Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.sendMessage.alreadySendingMessage", "Already sending message."));
        return;
      } else
        sendLock = true;
    }

    try {
      if (getNewMessageUI() != null) {
        getNewMessageUI().setBusy(true);
        UserProfile profile = getNewMessageUI().getSelectedProfile();
        InternetHeaders headers = getNewMessageUI().getMessageHeaders();

        String messageText = getNewMessageUI().getMessageText();

        String messageContentType = getNewMessageUI().getMessageContentType();

        if (getCryptoInfo().updateRecipientInfos(profile, headers)) {
          getNewMessageInfo().sendMessage(profile, headers, getCryptoInfo(), messageText, messageContentType);
        }
      }
    } catch (MessagingException me) {
      sendLock=false;
      getMessageUI().showError(Pooka.getProperty("Error.sendingMessage", "Error sending message:  "), me);
      getNewMessageUI().setBusy(false);
    }
  }

  /**
   * Called when the send succeeds.
   */
  public void sendSucceeded(boolean pSaveToSentFolder) {
    final NewMessageUI nmui = getNewMessageUI();
    if (nmui != null) {
      boolean closeDialog = true;

      if (pSaveToSentFolder) {
        // only close the dialog now if we don't have a sent folder to
        // save to.
        closeDialog = ! getNewMessageInfo().saveToSentFolder(getNewMessageUI().getSelectedProfile());
      }

      // if not saving to a sent folder, close the dialog.
      if ((! pSaveToSentFolder) ||  closeDialog) {
        Runnable runMe = new Runnable() {
            public void run() {
              Pooka.getUIFactory().clearStatus();
              nmui.setBusy(false);
              nmui.setModified(false);
              nmui.closeMessageUI();
            }
          };
        SwingUtilities.invokeLater(runMe);
      }
    }

  }

  /**
   * Called when the send fails.
   */
  public void sendFailed(OutgoingMailServer pMailServer, Exception e) {
    sendLock=false;
    Pooka.getUIFactory().clearStatus();
    final OutgoingMailServer mailServer = pMailServer;
    final Exception me = e;
    final NewMessageUI nmui = getNewMessageUI();
    if (nmui != null) {
      Runnable runMe = new Runnable() {
          public void run() {
            if (me instanceof MessagingException) {
              if (mailServer != null) {
                SendFailedDialog sfd = getNewMessageUI().showSendFailedDialog(mailServer, (MessagingException) me);
                if (sfd.resendMessage()) {
                  OutgoingMailServer newServer = sfd.getMailServer();
                  if (newServer != null) {
                    String action = sfd.getMailServerAction();
                    UserProfile profile = getNewMessageUI().getSelectedProfile();
                    if (action == SendFailedDialog.S_SESSION_DEFAULT) {
                      profile.setTemporaryMailServer(newServer);
                    } else if (action == SendFailedDialog.S_CHANGE_DEFAULT) {
                      Pooka.setProperty(profile.getUserProperty() + ".mailServer", newServer.getItemID());
                    }
                    newServer.sendMessage(getNewMessageInfo());
                  }
                } else if (sfd.getSaveToOutbox()) {
                  try {
                    mailServer.saveToOutbox(getNewMessageInfo());
                  } catch (OperationCancelledException oce) {
                  } catch (MessagingException outboxException) {
                    getMessageUI().showError(Pooka.getProperty("error.MessageUI.sendFailed", "Failed to send Message.") + "\n" + outboxException.getMessage());
                  }
                }
              } else {
                getMessageUI().showError(Pooka.getProperty("error.MessageUI.sendFailed", "Failed to send Message.") + "\n" + me.getMessage());
                me.printStackTrace(System.out);
              }
            } else {
              getMessageUI().showError(Pooka.getProperty("error.MessageUI.sendFailed", "Failed to send Message.") + "\n" + me.getMessage());
              me.printStackTrace(System.out);
            }
            nmui.setBusy(false);
          }
        };
      SwingUtilities.invokeLater(runMe);
    } else {
      Runnable runMe = new Runnable() {
          public void run() {
            Pooka.getUIFactory().showError(Pooka.getProperty("error.MessageUI.sendFailed", "Failed to send Message.") + "\n" + me.getMessage());
            me.printStackTrace(System.out);
          }
        };
      SwingUtilities.invokeLater(runMe);
    }
  }

  /**
   * Matches the currently selected UserProfile to the one set in the
   * NewMessageInfo.
   */
  public void matchUserProfile() {
    NewMessageUI nmui = getNewMessageUI();
    if (nmui != null) {
      try {
        String profileId = (String) getMessageInfo().getMessageProperty(Pooka.getProperty("Pooka.userProfileProperty", "X-Pooka-UserProfile"));
        if (profileId != null && ! profileId.equals("")) {
          UserProfile profile = Pooka.getPookaManager().getUserProfileManager().getProfile(profileId);
          if (profile != null)
            nmui.setSelectedProfile(profile);
        }
      } catch (MessagingException me) {
        // no big deal...  we can just have the default user selected.
      }
    }
  }

  /**
   * This attaches a file to a given message.  Really, all it does is
   * calls getFileToAttach(), and then sends that to attachFile().
   */
  public void attach() {
    File[] f = getFileToAttach();
    if (f != null) {
      NewMessageUI nmui = getNewMessageUI();
      nmui.setModified(true);
      for (int i = 0; i < f.length; i++)
        attachFile(f[i]);
    }
  }

  /**
   * This calls on the MessageUI to bring up a FileDialog to choose
   * the file to attach to the message.  If no choice is made, this
   * method returns null.
   */
  public File[] getFileToAttach() {
    return getNewMessageUI().getFiles(Pooka.getProperty("MessageUI.attachFileDialog.title", "Choose file to attach."), Pooka.getProperty("MessageUI.attachFileDialog.buttonText", "Attach"));
  }

  /**
   * This actually attaches the File to the Message.  Any errors are
   * sent to the MessageUI to display.
   *
   * This also sets the 'hasAttachment' property on the MessageUI
   * to true.
   */
  public void attachFile(File f) {
    try {
      getNewMessageInfo().attachFile(f);

      getNewMessageUI().attachmentAdded(getNewMessageInfo().getAttachments().size() -1);
    } catch (Exception e) {
      getMessageUI().showError(Pooka.getProperty("error.MessageUI.unableToAttachFile", "Unable to attach file."), Pooka.getProperty("error.MessageUI.unableToAttachFile.title", "Unable to Attach File."), e);
    }

  }

  /**
   * This removes the given Attachment from the list of attachments.
   * I figure that you're likely only to be removing attachments from
   * the attachment list itself, so you should be able to get the
   * correct underlying object.
   */
  public void detachFile(Attachment a) {
    int index = getNewMessageInfo().removeAttachment(a);
    if (index != -1)
      getNewMessageUI().attachmentRemoved(index);
  }

  /**
   * Saves this message as a draft version, if there is an Outbox
   * configured.
   */
  public void saveDraft() {
    // thread:  AwtEvent

    synchronized(this) {
      if (sendLock) {
        Pooka.getUIFactory().showStatusMessage(Pooka.getProperty("info.sendMessage.alreadySendingMessage", "Already sending message."));
        return;
      } else
        sendLock = true;
    }

    try {
      if (getNewMessageUI() != null) {
        getNewMessageUI().setBusy(true);

        final UserProfile profile = getNewMessageUI().getSelectedProfile();
        final InternetHeaders headers = getNewMessageUI().getMessageHeaders();

        final String messageText = getNewMessageUI().getMessageText();

        final String messageContentType = getNewMessageUI().getMessageContentType();

        OutgoingMailServer mailServer = profile.getMailServer();

        final FolderInfo fi = mailServer.getOutbox();

        if (fi != null) {
          net.suberic.util.thread.ActionThread folderThread = fi.getFolderThread();
          Action runMe = new AbstractAction() {
              public void actionPerformed(java.awt.event.ActionEvent e) {
                try {
                  getNewMessageInfo().saveDraft(fi, profile, headers, messageText, messageContentType);
                  saveDraftSucceeded(fi);
                } catch (OperationCancelledException oce) {
                  saveDraftFailed(oce);
                } catch (MessagingException me) {
                  saveDraftFailed(me);
                }
              }
            };
          folderThread.addToQueue(runMe, new java.awt.event.ActionEvent(this, 0, "saveDraft"));
        } else {
          saveDraftFailed(new MessagingException ("No outbox specified for default mailserver " + mailServer.getItemID()));
        }
      } else {
        sendLock = false;
      }
    } catch (MessagingException me) {
      sendLock=false;
      getMessageUI().showError(Pooka.getProperty("Error.sendingMessage", "Error sending message:  "), me);
      getNewMessageUI().setBusy(false);
    }

  }

  /**
   * Called when the save draft succeeds.
   */
  public void saveDraftSucceeded(FolderInfo outboxFolder) {
    final FolderInfo outbox = outboxFolder;
    final NewMessageUI nmui = getNewMessageUI();
    if (nmui != null) {
      Runnable runMe = new Runnable() {
          public void run() {
            nmui.setBusy(false);
            nmui.setModified(false);
            getMessageUI().showMessageDialog("Message saved to " +outbox.getFolderID(), "Draft Saved");
            getMessageUI().closeMessageUI();
          }
        };
      SwingUtilities.invokeLater(runMe);
    }
    sendLock=false;

  }

  /**
   * Called when the send fails.
   */
  public void saveDraftFailed(Exception e) {
    final Exception me = e;
    final NewMessageUI nmui = getNewMessageUI();
    if (nmui != null) {
      Runnable runMe = new Runnable() {
          public void run() {
            getMessageUI().showError(Pooka.getProperty("error.MessageUI.saveDraftFailed", "Failed to save message."), me);
            nmui.setBusy(false);
          }
        };
      SwingUtilities.invokeLater(runMe);
    }
    sendLock=false;
  }


  /**
   * a convenience method which returns the current MessageUI as
   * a NewMessageUI.
   */
  public NewMessageUI getNewMessageUI() {
    if (getMessageUI() instanceof NewMessageUI)
      return (NewMessageUI)getMessageUI();
    else
      return null;
  }

  /**
   * a convenience method which returns the current MessageInfo as
   * a NewMessageInfo.
   */
  public NewMessageInfo getNewMessageInfo() {
    return (NewMessageInfo) messageInfo;
  }

  /**
   * Returns the CryptoInfo for this proxy.
   */
  public NewMessageCryptoInfo getCryptoInfo() {
    return mCryptoInfo;
  }

  /**
   * Returns whether or not we should prompt the user to see if they really
   * want to close the window for this message.
   */
  public boolean promptForClose() {
    if (! Pooka.getProperty("Pooka.checkUnsentMessages", "false").equalsIgnoreCase("true")) {
      return false;
    }

    NewMessageUI nmui = getNewMessageUI();

    if (nmui != null) {
      return nmui.isModified();
    }

    return false;
  }

  public Action[] defaultActions = {
    new SendAction(),
    new AttachAction(),
    new SaveDraftAction(),
    //    new EncryptAction(),
    //    new ClearEncryptAction(),
    //    new SelectEncryptionKeyAction(),
    //    new SignAction(),
    //    new ClearSignAction(),
    //    new SelectSignatureKeyAction(),
    //    new AttachKeyAction(),
    //    new RemoveKeyAction()
    //Liao-
    //    ,
    new SelectNoCrypt(),
    new SelectSMIMESign(),
    new SelectSMIMEEncrypt(),
    new SelectSMIMEBoth(),
    new SelectPGPSign(),
    new SelectPGPEncrypt(),
    new SelectPGPBoth()
    //Liao+
  };

  public Action getAction(String name) {
    return (Action)commands.get(name);
  }

  public Action[] getActions() {
    return defaultActions;
  }

  public static Vector getUnsentProxies() {
    return allUnsentProxies;
  }

  class SendAction extends AbstractAction {
    SendAction() {
      super("message-send");
    }

    public void actionPerformed(ActionEvent e) {
      send();
    }
  }

  class AttachAction extends AbstractAction {
    AttachAction() {
      super("message-attach-file");
    }

    public void actionPerformed(ActionEvent e) {
      attach();
    }
  }

  class SaveDraftAction extends AbstractAction {
    SaveDraftAction() {
      super("message-save-draft");
    }

    public void actionPerformed(ActionEvent e) {
      saveDraft();
    }
  }


  /*  class EncryptAction extends AbstractAction {
      EncryptAction() {
      super("message-encrypt");
      }

      public void actionPerformed(ActionEvent e) {
      CryptoStatusDisplay csd = getMessageUI().getCryptoStatusDisplay();
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setEncryptMessage(NewMessageCryptoInfo.CRYPTO_YES);
      }

      final boolean forSignature = false;;
      if (getCryptoInfo().getEncryptionKey() == null &&
      getNewMessageUI().getSelectedProfile().getEncryptionKey(forSignature) == null) {
      try {
      java.security.Key cryptKey = selectPublicKey(
      Pooka.getProperty("Pooka.crypto.publicKey.forEncrypt",  "Select key to encrypt this message."),
      Pooka.getProperty("Pooka.crypto.publicKey.title", "Select public key"),
      forSignature);
      if (cryptKey != null) {
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setEncryptionKey(cryptKey);
      }
      }
      } catch (Exception ex) {
      getMessageUI().showError(ex.getMessage(), ex);
      }
      }
      }
      }
  */
  /*  class SignAction extends AbstractAction {
      SignAction() {
      super("message-sign");
      }

      public void actionPerformed(ActionEvent e) {
      CryptoStatusDisplay csd = getMessageUI().getCryptoStatusDisplay();
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setSignMessage(NewMessageCryptoInfo.CRYPTO_YES);
      }

      final boolean forSignature = true;
      if (getCryptoInfo().getSignatureKey() == null &&
      (getDefaultProfile() == null ||
      getDefaultProfile().getEncryptionKey(forSignature) == null)) {
      try {
      java.security.Key signKey = selectPrivateKey(
      Pooka.getProperty("Pooka.crypto.privateKey.forSig", "Select key to sign this message."),
      Pooka.getProperty("Pooka.crypto.privateKey.title", "Select private key"),
      forSignature);
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setSignatureKey(signKey);
      }
      } catch (Exception ex) {
      getMessageUI().showError(ex.getMessage(), ex);
      }

      }
      }
      }
  */
  /*  class SelectSignatureKeyAction extends AbstractAction {
      SelectSignatureKeyAction() {
      super("message-select-sig-key");
      }

      public void actionPerformed(ActionEvent e) {
      CryptoStatusDisplay csd = getMessageUI().getCryptoStatusDisplay();
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setSignMessage(NewMessageCryptoInfo.CRYPTO_YES);
      }

      final boolean forSignature = true;
      try {
      java.security.Key signKey = selectPrivateKey(
      Pooka.getProperty("Pooka.crypto.privateKey.forSig", "Select key to sign this message."),
      Pooka.getProperty("Pooka.crypto.privateKey.title", "Select private key"),
      forSignature);
      if (signKey != null) {
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setSignatureKey(signKey);
      }
      }
      } catch (Exception ex) {
      getMessageUI().showError(ex.getMessage(), ex);
      }
      }
      }

      class SelectEncryptionKeyAction extends AbstractAction {
      SelectEncryptionKeyAction() {
      super("message-select-crypt-key");
      }

      public void actionPerformed(ActionEvent e) {
      CryptoStatusDisplay csd = getMessageUI().getCryptoStatusDisplay();
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setEncryptMessage(NewMessageCryptoInfo.CRYPTO_YES);
      }

      final boolean forSignature = false;
      try {
      java.security.Key cryptKey = selectPublicKey(
      Pooka.getProperty("Pooka.crypto.publicKey.forEncrypt", "Select key to encrypt this message."),
      Pooka.getProperty("Pooka.crypto.publicKey.title", "Select public key"),
      forSignature);
      if (cryptKey != null) {
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setEncryptionKey(cryptKey);
      }
      }
      } catch (Exception ex) {
      getMessageUI().showError(ex.getMessage(), ex);
      }
      }
      }

      class ClearEncryptAction extends AbstractAction {
      ClearEncryptAction() {
      super("message-clear-encrypt");
      }

      public void actionPerformed(ActionEvent e) {
      CryptoStatusDisplay csd = getMessageUI().getCryptoStatusDisplay();
      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setEncryptMessage(NewMessageCryptoInfo.CRYPTO_NO);((NewMessageCryptoDisplay) csd).setEncryptionKey(null);
      }
      }
      }

      class ClearSignAction extends AbstractAction {
      ClearSignAction() {
      super("message-clear-signature");
      }

      public void actionPerformed(ActionEvent e) {
      CryptoStatusDisplay csd = getMessageUI().getCryptoStatusDisplay();

      if (csd instanceof NewMessageCryptoDisplay) {
      ((NewMessageCryptoDisplay) csd).setSignMessage(NewMessageCryptoInfo.CRYPTO_NO);
      ((NewMessageCryptoDisplay) csd).setSignatureKey(null);
      }
      }
      }

      class AttachKeyAction extends AbstractAction {
      AttachKeyAction() {
      super("message-attach-crypt-key");
      }

      public void actionPerformed(ActionEvent e) {
      CryptoStatusDisplay csd = getMessageUI().getCryptoStatusDisplay();

      if (csd instanceof NewMessageCryptoDisplay) {
      try {
      String a = Pooka.getProperty("Pooka.crypto.publicKey.forAttach", "Select key to attach to message.");
      String b = Pooka.getProperty("Pooka.crypto.publicKey.title", "Select public key");
      java.security.Key cryptKey = selectPublicKey(a,b,false);
      if(cryptKey == null)
      cryptKey = selectPublicKey(a,b,true);
      ((NewMessageCryptoDisplay) csd).attachEncryptionKey(cryptKey);
      } catch (Exception ex) {
      getMessageUI().showError(ex.getMessage(), ex);
      }
      }
      }
      }

      class RemoveKeyAction extends AbstractAction {
      RemoveKeyAction() {
      super("message-remove-crypt-key");
      }

      public void actionPerformed(ActionEvent e) {
      CryptoStatusDisplay csd = getMessageUI().getCryptoStatusDisplay();

      if (csd instanceof NewMessageCryptoDisplay) {
      try {
      String a = Pooka.getProperty("Pooka.crypto.publicKey.forAttach", "Select key to attach to message.");
      String b = Pooka.getProperty("Pooka.crypto.publicKey.title", "Select public key");
      java.security.Key cryptKey = selectPublicKey(a,b,false);
      if(cryptKey == null)
      cryptKey = selectPublicKey(a,b,true);
      ((NewMessageCryptoDisplay) csd).removeEncryptionKey(cryptKey);
      } catch (Exception ex) {
      getMessageUI().showError(ex.getMessage(), ex);
      }
      }
      }
      }
  */

  //Liao-
  class SelectNoCrypt extends AbstractAction{
    SelectNoCrypt(){ super(NO_CRYPTO_CMD); }
    public void actionPerformed(ActionEvent e) {
      mCryptoInfo.setCryptoType(NewMessageCryptoInfo.NO_CRYPTO);}
  }
  class SelectSMIMESign extends AbstractAction{
    SelectSMIMESign(){ super(SMIME_Sign_CMD); }
    public void actionPerformed(ActionEvent e) {
      mCryptoInfo.setCryptoType(NewMessageCryptoInfo.SMIME_SIGN);}
  }
  class SelectSMIMEEncrypt extends AbstractAction{
    SelectSMIMEEncrypt(){ super(SMIME_Encrypt_CMD); }
    public void actionPerformed(ActionEvent e) {
      mCryptoInfo.setCryptoType(NewMessageCryptoInfo.SMIME_ENCRYPT);}
  }
  class SelectSMIMEBoth extends AbstractAction{
    SelectSMIMEBoth(){ super(SMIME_Both_CMD); }
    public void actionPerformed(ActionEvent e) {
      mCryptoInfo.setCryptoType(NewMessageCryptoInfo.SMIME_BOTH);}
  }

  class SelectPGPSign extends AbstractAction{
    SelectPGPSign(){ super(PGP_Sign_CMD); }
    public void actionPerformed(ActionEvent e) {
      mCryptoInfo.setCryptoType(NewMessageCryptoInfo.PGP_SIGN);}
  }
  class SelectPGPEncrypt extends AbstractAction{
    SelectPGPEncrypt(){ super(PGP_Encrypt_CMD); }
    public void actionPerformed(ActionEvent e) {
      mCryptoInfo.setCryptoType(NewMessageCryptoInfo.PGP_ENCRYPT);}
  }
  class SelectPGPBoth extends AbstractAction{
    SelectPGPBoth(){ super(PGP_Both_CMD); }
    public void actionPerformed(ActionEvent e) {
      mCryptoInfo.setCryptoType(NewMessageCryptoInfo.PGP_BOTH);}
  }

  public static final String NO_CRYPTO_CMD       = "message-crypto-no";
  public static final String SMIME_Sign_CMD      = "message-crypto-smime_sign";
  public static final String SMIME_Encrypt_CMD   = "message-crypto-smime_encrypt";
  public static final String SMIME_Both_CMD      = "message-crypto-smime_both";
  public static final String PGP_Sign_CMD        = "message-crypto-pgp_sign";
  public static final String PGP_Encrypt_CMD     = "message-crypto-pgp_encrypt";
  public static final String PGP_Both_CMD        = "message-crypto-pgp_both";

  //Liao+
}





TOP

Related Classes of net.suberic.pooka.gui.NewMessageProxy

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.