logo

Sending HTML mail via SMTP part 2

Okay so in the first article on this topic we've showed that it's possible to send mail through the Domino server and have it appear to the user in HTML format. What we need to do now is see exactly how we do this.d

At this point it's worth noting that the code we use is going to be Java. This is one of the times where we can show ways in which Java is, in more ways than one, far superior to its LotusScript partner. LotusScript is fine up to a point but when we need to go a step further and do things like accessing network protocols it's the turn of Java.

The third article in this series has an example database that shows how to use them. In the database I have added the required Java to a Script Library that is included in the Agent project-files. This agent is run as a WebQuerySave event when the user saves the form called Socket. Using the information in this form and some from the Application Settings view the agent has all it needs to create a fully-formed message. Let's look at how we do this.

The Code:

With Java we can create a new Socket to a specific port on a server and then pass data back and forth through this connection. In order to do this we first need to import the necessary code libraries:

import java.io.*;
import java.net.*;

Now we can set up the connection and the two required streams through which we post the data:
vSocket = new Socket( sHostAddress, 25);
vInStream = new BufferedReader( new InputStreamReader(vSocket.getInputStream()));
vOutStream = new DataOutputStream(vSocket.getOutputStream());

Once we have these two streams opened it's a simple case of sending the correct lines of text, in the correct order, out via the output stream. In the last article I showed the order in which each line needs to be sent to the SMTP server. What I didn't show was how the client expects certain results to be received from the server. Some lines of data can simply be sent to the server ad hoc but some need to be accompanied by a reply that lets the client know that it has received the command and all is okay. If this isn't the case then we handle the error by cancelling the message sending and returning an error to the browser.

To send a command to the server we use the sendCommand method:
protected void sendCommand( String cmd ) throws Exception {
 vOutStream.writeBytes( cmd + "\r\n" );
}

To check the server's response we use the checkReply method:
protected boolean checkReply( String sRspNum ) {
 String sResponse = null;
 sResponse = vInStream.readLine();
 return sResponse.startsWith( sRspNum + " " );
}

So, to send the DATA command, from which we expect to receive the code 354 to let us know it's okay to send the body, we would use:
sendCommand( "DATA" );
  if ( !checkReply( "354" ) ) 
   killSend( 1005, "Didn\'t like our DATA command!" );

Notice the extra method called killSend. This is used simply as a way to terminate the whole sending by throwing an error that returns the agent a code with which it can let the user know there was a problem and what it was.

Not all of the lines we send to the server need us to check the code returned. When this is the case we simply use the sendText method:
public void sendText( String text ) throws Exception {
 vOutStream.writeBytes( text + "\r\n" );
}

With which we can send repeated lines of text to the server, like so:
sendText( "<html>" );
sendText( "<body>" );

That's about all we need really. All that is required now to actually build the message is the code that lives in the WebQuerySave agent. Here it is:
PrintWriter pw = getAgentOutput();
String dbpath=null;
try {
  Session vNotesSession = getSession();
  AgentContext vNotesAgentContext = vNotesSession.getAgentContext();
  Database vNotesDatabase = vNotesAgentContext.getCurrentDatabase();
  View vNotesView = vNotesDatabase.getView("luASK");
  Document vNotesDocument = vNotesAgentContext.getDocumentContext( );			
  Document vSettingDoc = vNotesView.getDocumentByKey("App Setting\\SMTPHost");
			
  if (vSettingDoc != null)
   sSMTPHostAddress = vSettingDoc.getItemValueString("ASValue");

  String sToShow = vNotesDocument.getItemValueString("ToShow");
  Vector vToTrue = vNotesDocument.getItemValue("SendTo");
  String sFromShow = vNotesDocument.getItemValueString("FromShow"); 
  String sFromTrue = vNotesDocument.getItemValueString("From");;
  String sSubject = vNotesDocument.getItemValueString("Subject");
  Vector vBody = vNotesDocument.getItemValue("Body");
		
  dbpath = vNotesDocument.getItemValueString("DBPath");
		
  HtmlMessage msg = new HtmlMessage( sSMTPHostAddress );

  msg.sendMessage( sFromTrue, sFromShow, vToTrue, sToShow, sSubject, vBody );
			
  //Assuming all must be okay, let's let the user know.
  pw.println( "[/" + dbpath + "/message?OpenForm&msgid=1000]");
			
 } catch ( SMTPException e) {
  System.err.println( e.getMessage() );
  pw.println( "[/" + dbpath + "/message?OpenForm&msgid=" + e.getCode() + "]");
 } catch( Exception e) {
  e.printStackTrace();
 }

In the code we have created a new HtmlMessage object passing in to it the name of the server to use. We then call this object's sendMessage method and pass in to it all the information it requires. Notice that the list of addressees and the body are both Vectors. This is because the fields on the document are multi-value. We rely on the sendMessage method to convert these lists in to strings that it can use.

Considerations:

The example I've used in the database of a form with all the fields required in order to send a message isn't perfect. It works and is a good example of how this method can be used but it's not likely to happen this way in the real world.

How you use this code and in what situation is down to you and can suit any number of different circumstances. Hopefully the way in which I've built the example database means that it will be easy for you to tailor it to your own needs. In the next article on this topic I will discuss a powerful method of sending whole pages from a Domino database simply by providing the code with a URL.

Naming problems:

You don't have to be a genius to see that the names used in the SendTo field on the Socket form of the database are not what we are used to seeing. The format we need to use when talking to SMTP servers is name@domain. We aren't allowed any spaces in here. Hence for any user in the Domino Directory we need to know there ShortName and their mail domain. To do this for a user in the real world we are probably going to have to find their Person document via a DBLookup and extract these two fields from it.

Domino vs Exchange:

Despite my allegiance to the Domino server this is another occasion where I have to admit that Microsoft reign supreme.

If by now you've tried sending mail with my example database and have tried adding an <img> tag you may have had problems. The Notes Mail Client isn't too hot when it comes to handling URLs - especially if they are relative.

If you are working in an environment where Domino and Exchange cohabit you will find this a lot easier. When you use Exchange as the target SMTP server and the Outlook client to read the mail you can begin to get really fancy. The Outlook client is a lot better at handling the HTML and even, to some extent, JavaScript. This I expect is due to Outlook using IE as the rendering device for the mail whereas Notes uses its own methods.

When using Outlook there is an extra line of code supported in the mails header which lets us set the equivalent of the <base href> tag in HTML. To do this we add another line to the mail's header like:

Content-Base: http://www.codestore.info/A55692/store.nsf/;

Then in the HTML we can add HTML for an image element like this:

<a href="all?OpenView"><img src="codestore.gif" /></a>

This is extremely useful when we want to send a whole lot of HTML as I mentioned earlier by using a URL as the source of this code. This is what I'll be talking about in the next article on this topic. Don't hold your breath though, this could take me a week or so ;-)

<<< Part 1
Part 2
>>> Part 3

Feedback

  1. Another Great Article

    The first article you wrote was great and I was actually able to get the few examples working on our servers, so I was happy to see the second article. Looks good and I can't wait to try it. We almost bought a Notes add-on product to send HTML mail, but using this solution we may be able to avoid the purchase. Keep up the good work!

    • avatar
    • didier
    • Mon 1 Apr 2002

    Someone can explain me ?

    What's the problem with sending HTML mail through the mail router ?

    1. Re: Someone can explain me ?

      Sorry, I was very vague in my crticism of the Notes Mail Router.

      What I meant was that it's not a simple case of creating a HTML mail with code similar to the following: [<span class="lotusscript">] Set doc = db.CreateDocument doc.Form = "Memo" doc.Subject = "This is in HTML(?)" doc.MIME_Version = "1.0;" 'etc [</span>] However you set the value of these fields is of no consequence as they will get altered by the server as it routes the mail.

      Hope that clears it up..

      Jake -codestore

    • avatar
    • Sam
    • Tue 2 Apr 2002

    You've managed to do the impossible...

    ...you've explained Java code in a way that I can understand. =) Thanks for the great articles, I'm going to try them out this morning.

  2. A fantastic post..

    Many thanks for the clear explanation and also the extremely useful test harness - it makes a big difference if you can see it in action after a few minutes. I look forward to the third article.....

    1. Re: A fantastic post..

      Cheers David,

      I was beginning to think I'd written that article in an alien language. Feedback has been slow to what I thought was one of the things you lot were begging for.

      Oh well, you can't win them all ;-)

      Part 3 should be ready by the end of the week.

      Jake -codestore

  3. Please explain the Java errors I received

    I am new at Java, so forgive me if this is a stupid question. I downloaded you sample database and copied the forms, pages and agents to my database. I changed the SMTPHostAddress to mine and created my own HTML test message. I ran the WQS-SendMSG agent and no message was sent. When I checked the Java Console this is the error message I received: java.lang.NullPointerException:

    at JavaAgent.NotesMain(JavaAgent.java:25)

    at lotus.domino.AgentBase.runNotes(AgentBase.java:160)

    at lotus.domino.NotesThread.run(NotesThread.java:203). Can you explain what this means and possibly how I can fix this? Thanks, Jeff

    1. Re: Please explain the Java errors I received

      Null pointer means you are referencing a variable that has no value, obviously.

      Which one it is I don't know as I can't see what you've done. Try doing some debugging. Use the line:

      System.out.println( nameOfYourObject );

      In various points of your code and watch the Server Console to see what they are. My guess is that it's the SMTPHost name....

      Jake -codestore

      Show the rest of this thread

    2. Re: Please explain the Java errors I received

      Hi,

      i too have the same problem,

      i created one Newmail agents which which has to execute when ever a new mail comes. but its giving the same error like your's

      if u know the answer at any case please update me.

      Thanks&Regards Mahesh B

  4. Question about java agent.

    Dear Jake,

    I'm new at java and i need send an html email by an agent. I'm try to run agent from the agent view with the code below (is copy from your!!!).

    The problem is, after click the RUN at action menu, give me this message error:

    "Started running agent 'WQS-SendURL' on 16/08/2002 15:03:24 ERROR: Server error: Entry not found in index Ran Java Agent Class Done running agent 'WQS-SendURL' on 16/08/2002 15:03:24 "

    Is because it can't find the java.io / java.net / java.util ???

    Any help is welcome!!!

    Thks

    Daniela

    import lotus.domino.*; import java.io.*; import java.net.*; import java.util.*;

    public class JavaAgent extends AgentBase {

    public void NotesMain() {

    PrintWriter pw = getAgentOutput(); String dbpath=null;

    try { Session vNotesSession = getSession(); AgentContext vNotesAgentContext = vNotesSession.getAgentContext(); Database vNotesDatabase = vNotesAgentContext.getCurrentDatabase(); String sSMTPHostAddress = "smtp.acme.com"; Vector vToTrue = "joao@acme.com"; String sToShow = "Joao"; String sFromTrue = "peter@acme.com"; String sFromShow = "Santa Maria"; String sSubject = "Envio de eMail SMTP"; URL vURLToMail = new URL("www.lotus.com"); HtmlMessage msg = new HtmlMessage( sSMTPHostAddress ); msg.sendMessage( sFromTrue, sFromShow, vToTrue, sToShow, sSubject, vURLToMail ); } catch ( MalformedURLException e) { System.err.println( "Malformed URL: " + e.getMessage() ); //pw.println( "error 1"); } catch ( UnknownHostException e) { System.err.println( "Can\'t connect to host: " + e.getMessage() ); //pw.println( "error 1"); } catch ( FileNotFoundException e) { System.err.println( "404 Can\'t find specified resource: " + e.getMessage() ); //pw.println( "error 3"); } catch ( SMTPException e) { System.err.println( e.getMessage() ); //pw.println( "error 4"); } catch( Exception e) { e.printStackTrace(); } } }

    • avatar
    • Johan Känngård
    • Wed 18 Dec 2002

    Use JavaMail instead

    I recently had some real problems with the Domino router, so I looked at the [<a href="http://java.sun.com/products/javamail/">Java Mail API </a> from <a href="http://www.sun.com">Sun</a>, and it works great! Have a look at <a href="https://dev.kanngard.net/dev/home.nsf/ArticlesByTitle/SMTPJavaMailAgent.ht ml">my short tip</a>].

    I am about to write my own router task now, so I have more control of the mail transfering. :-)

    - Johan

  5. Minor bug in the Example Database

    There is a minor bug in the sample database that causes an infinite loop if there are no messages to send (and 100% CPU usage).

    Below is the fix, simply the 'nextdocument' call is made from within the if loop, where it shouldn't be. This is applied at the end of 'SendQueue' agent.

    ---<snip>-------- } //try inside while loop } //Get next document and delete the curent one vNotesDocument = vNotesView.getNextDocument( vNotesDocument ); } //while loop } catch( Exception e) { System.err.println( e.getMessage() ); //e.printStackTrace(); } //outer try } //notes main } //;-)))

    ---<snip>--------

    Great program by the way!!! Thank you very much.

    • avatar
    • Dmitriy
    • Fri 2 Apr 2004

    HTML source instead of HTML rendering

    I used your example and receive HTML source instead of HTML rendering, any clue?

  6. 6.55 server upgrade

    Our server was upgraded to 6.55, and now the HTML system no longer works - receiving no protocol error message.

    Can you help?

    • avatar
    • Richard
    • Tue 17 Jan 2012

    Jake - this is a fantastic post as already mentioned, unfortunately I am receiving a "Malformed URL: no protocol" error message. This same application works perfect with Domino 6.5.4, but the same is not true for 8.5.1. The other unfortunate thing here is that I am not well versed in Java, thus I am having an issue getting to the bottom of the problem. Can you help me out?

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:



Navigate other articles in the category "Java"

« Previous Article Next Article »
Domino, XML-RPC and Java - An Example   Sending HTML mail via SMTP part 3

About This Article

Author: Jake Howlett
Category: Java
Keywords: SMTP; Mail; API; Socket; HTML;

Options

Feedback
Print Friendly

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »