31 Jan 2013

Copy Constructor in Java

Implement Copy Constructor in Java.

When an object is copied to the other using assignment operator, reference of the object, instead of value of the object, is copied. Therefore changes in one object apply to another.

Copy Constructor can be used to avoid the aforesaid problem.

Java Source Code

class Ball { private String color; private double radius; // A normal constructor public Ball(double radius, String color) { this.radius = radius; this.color = color; } // Copy Constructor Ball(Ball c) { System.out.println("Copy constructor called."); this.radius = c.radius; this.color = c.color; } public void setColor(String color) { this.color = color; } public void setRadius(double radius) { this.radius = radius; } // Overriding the toString of Object class @Override public String toString() { return "Radius: " + this.radius + ", Color: " + this.color; } } public class Box { public static void main(String[] args) { Ball b1 = new Ball(10, "Red"); System.out.println("Original values of b1:"); System.out.println(b1); System.out.println("*******************************************"); /** * Call a copy constructor. * Note that the values of c1 ARE NOT modified. */ Ball b2 = new Ball(b1); b2.setRadius(20); b2.setColor("Black"); System.out.println("After modification of b2 values,"); System.out.println("which is created using copy constructor:"); System.out.println("[b1] " + b1); System.out.println("[b2] " + b2); System.out.println("*******************************************"); /** * Copy by reference. * Note that the values of c1 ARE modified. */ Ball b3 = b1; b3.setRadius(20); b3.setColor("Black"); System.out.println("After modification of b3 values,"); System.out.println("which is created using assignment operator:"); System.out.println("[b1] " + b1); System.out.println("[b2] " + b2); System.out.println("[b3] " + b3); System.out.println("*******************************************"); /** * Changes of c1 values applied to c3. */ b1.setRadius(30); b1.setColor("Pink"); System.out.println("After modification of b1 values:"); System.out.println("[b1] " + b1); System.out.println("[b2] " + b2); System.out.println("[b3] " + b3); System.out.println("*******************************************"); } }

Output

Original values of b1: Radius: 10.0, Color: Red ******************************************* After modification of b2 values, which is created using copy constructor: [b1] Radius: 10.0, Color: Red [b2] Radius: 20.0, Color: Black ******************************************* After modification of b3 values, which is created using assignment operator: [b1] Radius: 20.0, Color: Black [b2] Radius: 20.0, Color: Black [b3] Radius: 20.0, Color: Black ******************************************* After modification of b1 values: [b1] Radius: 30.0, Color: Pink [b2] Radius: 20.0, Color: Black [b3] Radius: 30.0, Color: Pink *******************************************

29 Jan 2013

Enable Maven in Existing Eclipse Projects

A hands-on guide to enable Maven in existing Java projects in Eclipse.

In order to facilitate the build process in the software development life cycle, Maven can be used as a software project management and comprehension tool.

Projects are first configured as Maven-enabled. The POM (Project Object Model) file, which is an XML file (pom.xml), contains information about the project and configuration details used by Maven to build project. Configuration includes project dependencies, the plugins or goals that can be executed, the build profiles and so on.

Figure 1: Architecture of the Development Environment

Installation

  1. Install and Configure Maven.

Design

Suppose that three projects are created: myapp-common, myapp-web and myapp-admin.  As shown in Figure 4, myapp-web and myapp-admin have dependency on myapp-common, and this is configured in the POM file.

Figure 4: Projects Dependancies

Implementation

  1. Enable Maven for an existing Eclipse Project.
    1. Execute the following goal to create a maven project:
      mvn archetype:generate -DgroupId=com.blogspot.adaprognotebook-DartifactId=myapp-common -DarchetypeArtifactId=maven-archetype-quickstart mvn archetype:generate -DgroupId=com.blogspot.adaprognotebook-DartifactId=myapp-web -DarchetypeArtifactId=maven-archetype-webapp -Dpackage=war mvn archetype:generate -DgroupId=com.blogspot.adaprognotebook-DartifactId=myapp-admin -DarchetypeArtifactId=maven-archetype-webapp -Dpackage=war
    2. A directory with following structure is created for archetypes maven-archetype-quickstart:
      myapp-common |-- pom.xml |-- src |-- main |-- java |-- resources |-- test |-- java
    3. A directory with following structure is created for archetypes maven-archetype-webapp:
      myapp-web |-- pom.xml |-- src |-- main |-- resources |-- webapp |-- WEB-INF |-- web.xml |-- index.jsp myapp-admin |-- pom.xml |-- src |-- main |-- resources |-- webapp |-- WEB-INF |-- web.xml |-- index.jsp
    4. Create a /src/main/java directory in the myapp-web and myapp-admin projects.  Put the java source files into the /src/main/java directory of the myapp-common, myapp-web and myapp-admin projects appropriately.
    5. Put all the web content files into the /src/main/webapp directory of the myapp-web and myapp-admin projects appropriately.
    6. Generate the eclipse configuration files such that the project is eclipse-enabled:
      mvn eclipse:clean eclipse:eclipse -Dwtpversion=2.0
      The following eclipse configuration files will be generated:
      • .project and .classpath files
      • .setting/org.eclipse.jdt.core.prefs with project specific compiler settings
      • various configuration files for WTP (Web Tools Project), if the parameter wtpversion is set to a valid version (WTP configuration is not generated by default)
    7. Import the existing eclipse project from the File System (see Figure 5).
      Figure 5: Import existing projects into Workspace

    8. Configure eclipse to use the JRE of JDK (see Figure 6).
      Figure 6: Configure Installed JRE
    9. Add the following libraries in Proejct -> Preference -> Java Build Path:
      • Server Runtime
      • Web App Libraries
  2. Configure the connection to the DEV Nexus Server
    1. In the development client PC, open ${USER_HOME}\.m2\settings.xml using a text editor. If it does not exist, copy one from ${MAVEN_HOME}\conf\settings.xml.
    2. Add the following lines in the <servers> section:
      <servers> <server> <id>nexus-releases</id> <username>deployment</username> <password>test123</password> </server> <server> <id>nexus-snapshots</id> <username>deployment</username> <password>test123</password> </server> </servers>
    3. Add the following lines in the section:
      <mirror> <id>nexus</id> <name>nexus public mirror</name> <url>http://{Host of the Nexus server}/nexus/content/groups/public/</url> <mirrorOf>*</mirrorOf> </mirror>
    4. In the POM file, where deployment to Nexus Server is required, add the following lines under the section:
      <distributionManagement> <repository> <id>nexus-snapshots</id> <name>Samvo Repository for Maven</name> <url>http://{Host of the Nexus server}/nexus/content/repositories/snapshots/</url> </repository> </distributionManagement>
  3. Configure the connection from Maven of the Build Server to the Development Tomcat Server
    1. In Build Server, open ${USER_HOME}\.m2\settings.xml using a text editor. If it does not exist, copy one from ${MAVEN_HOME}\conf\settings.xml.
    2. Add the following lines in the section:
      <server> <id>tomcat</id> <username>ad</username> <password>test123</password> </server>
References
  1. Maven, http://maven.apache.org/
  2. Introduction to Archetypes, http://maven.apache.org/guides/introduction/introduction-to-archetypes.html
  3. Hudson, http://hudson-ci.org/
  4. Maven Central Repository Browser, http://search.maven.org/
  5. MVN Repository, http://mvnrepository.com/
  6. MVN Browser, http://www.mvnbrowser.com/
  7. Using Maven profiles and resource filtering, http://www.manydesigns.com/documentation/tutorials/using-maven-profiles-and-resource-filtering.html

XML Parsing

Since Oracle 9i Release 1 (9.0.1), a new datatype, XMLType, has been introduced to facilitate native handling of XML data in the database.

The following example demonstrate how to read an XML file using the XMLType.

XML

<?xml version="1.0"?> <customer_list> <sales_person_staff_no>201201234</sales_person_staff_no> <sales_person_name>Ada Cheng</sales_person_name> <customer> <customer_code>1234</customer_code> <customer_name>ABC Trading Co., Ltd.</customer_name> <contact> <contact_name>Mickey Mouse</contact_name> <phone_no>987654321</phone_no> </contact> <contact> <contact_name>Donald Duck</contact_name> <phone_no>987654322</phone_no> </contact> </customer> <customer> <customer_code>1235</customer_code> <customer_name>Sunshine Co., Ltd.</customer_name> <contact> <contact_name>Felix the Cat</contact_name> <phone_no>987654322</phone_no> </contact> </customer> </customer_list>

PL/SQL

create or replace PROCEDURE SP_XML_PARSING ( as_if_dir IN VARCHAR2, /* Oracle Directory of the XML File */ as_if_name IN VARCHAR2 /* File Name of the XML File */ ) AS /************************************************************************ NAME: SP_XML_PARSING PURPOSE: Parse an XML file. ************************************************************************/ CURSOR c_customer_list (f_xml XMLTYPE) IS SELECT customer_list.* FROM XMLTable('/customer_list' passing f_xml COLUMNS STAFF_NO VARCHAR2(20) path 'sales_person_staff_no', STAFF_NAME VARCHAR2(50) path 'sales_person_name', CUSTOMERS XMLTYPE path 'customer' ) AS customer_list; CURSOR c_customer (f_xml XMLTYPE) IS SELECT customer.* FROM XMLTable('/customer' passing f_xml COLUMNS CUSTOMER_CD VARCHAR2(20) path 'customer_code', CUSTOMER_NAME VARCHAR2(100) path 'customer_name', CONTACTS XMLTYPE path 'contact' ) AS customer; CURSOR c_contacts (f_xml XMLTYPE) IS SELECT contacts.* FROM XMLTable('/contact' passing f_xml COLUMNS NAME VARCHAR2(50) path 'contact_name', PHONE_NO VARCHAR2(50) path 'phone_no' ) AS contacts; TYPE lt_customer_list IS TABLE OF c_customer_list%ROWTYPE INDEX BY BINARY_INTEGER; TYPE lt_customer IS TABLE OF c_customer%ROWTYPE INDEX BY BINARY_INTEGER; TYPE lt_contacts IS TABLE OF c_contacts%ROWTYPE INDEX BY BINARY_INTEGER; la_customer_list lt_customer_list; la_customer lt_customer; la_contacts lt_contacts; lx_if XMLTYPE; BEGIN lx_if := XMLTYPE(bfilename(as_if_dir, as_if_name), nls_charset_id('AL32UTF8')); -- Customer List -- OPEN c_customer_list(lx_if); LOOP FETCH c_customer_list BULK COLLECT INTO la_customer_list; FOR i IN 1..la_customer_list.COUNT LOOP dbms_output.put_line('Staff No.: ' || la_customer_list(i).STAFF_NO); dbms_output.put_line('Staff Name: ' || la_customer_list(i).STAFF_NAME); dbms_output.put_line('**********************'); -- Customers -- IF c_customer%ISOPEN THEN CLOSE c_customer; END IF; OPEN c_customer(la_customer_list(i).CUSTOMERS); LOOP FETCH c_customer BULK COLLECT INTO la_customer; FOR j IN 1..la_customer.COUNT LOOP dbms_output.put_line('Cusomter Code: ' || la_customer(j).CUSTOMER_CD); dbms_output.put_line('Cusomter Name: ' || la_customer(j).CUSTOMER_NAME); dbms_output.put_line('----------------------'); -- Contacts -- IF c_contacts%ISOPEN THEN CLOSE c_contacts; END IF; OPEN c_contacts(la_customer(j).CONTACTS); LOOP FETCH c_contacts BULK COLLECT INTO la_contacts; FOR k IN 1..la_contacts.COUNT LOOP dbms_output.put_line('Contact Name: ' || la_contacts(k).NAME); dbms_output.put_line('Phone No.: ' || la_contacts(k).PHONE_NO); dbms_output.put_line('######################'); END LOOP; EXIT WHEN c_contacts%NOTFOUND; END LOOP; -- End Contacts -- END LOOP; EXIT WHEN c_customer%NOTFOUND; END LOOP; -- End Customers -- END LOOP; EXIT WHEN c_customer_list%NOTFOUND; END LOOP; -- End Customer List -- CLOSE c_contacts; CLOSE c_customer; CLOSE c_customer_list; END;

Output

Staff No.: 201201234 Staff Name: Ada Cheng ********************** Cusomter Code: 1234 Cusomter Name: ABC Trading Co., Ltd. ---------------------- Contact Name: Mickey Mouse Phone No.: 987654321 ###################### Contact Name: Donald Duck Phone No.: 987654322 ###################### Cusomter Code: 1235 Cusomter Name: Sunshine Co., Ltd. ---------------------- Contact Name: Felix the Cat Phone No.: 987654322 ######################

References

  1. Using XMLType, http://docs.oracle.com/cd/B10500_01/appdev.920/a96620/xdb04cre.htm

28 Jan 2013

Bulk Binding: BULK COLLECT

Use BULK COLLECT to retrieve large volume of data by using a single operation to bind the DML statements to whole collections, reducing the number of context switches, and hence greatly increases the performance.

In my experience in reading an XML file with more than 20,000 records, it took more than an hour to parse the data using an ordinary FOR loop.  However, after using the BULK COLLECT, it is greatly reduced to less than 2 minutes.

create or replace PROCEDURE SP_BULK_COLLECT AS /********************************************************************* NAME: SP_BULK_COLLECT PURPOSE: Example for fetching the record in large volume. *********************************************************************/ CURSOR c_products IS SELECT PRODUCT_CD FROM TBL_PRODUCT ORDER BY PRODUCT_CD; TYPE lt_products IS TABLE OF c_products%ROWTYPE INDEX BY BINARY_INTEGER; la_products lt_products; BEGIN OPEN c_products; LOOP FETCH c_products BULK COLLECT INTO la_products LIMIT 100; FOR i IN 1..la_products.COUNT LOOP dbms_output.put_line(la_products(i).PRODUCT_CD); END LOOP; EXIT WHEN c_products%NOTFOUND; END LOOP; CLOSE c_products; END;

References

  1. Introduction to Bulking, http://www.dba-oracle.com/plsql/t_plsql_bulking.htm

24 Jan 2013

Reversing the loop: REVERSE

Demonstrate the use of REVERSE keyword in a LOOP.

create or replace PROCEDURE SP_REVERSE_LOOP AS /********************************************************************* NAME: SP_REVERSE_LOOP PURPOSE: An example to reverse a looping. *********************************************************************/ la_nums  DBMS_SQL.VARCHAR2A; BEGIN FOR i IN 1..10 LOOP la_nums(i) := i * i; END LOOP; FOR i IN 1..10 LOOP dbms_output.put_line(i || ': ' || la_nums(i)); END LOOP; dbms_output.put_line('******************'); FOR i IN 10..1 LOOP dbms_output.put_line(i || ': ' || la_nums(i)); END LOOP; dbms_output.put_line('******************'); FOR i IN REVERSE 1..10 LOOP dbms_output.put_line(i || ': ' || la_nums(i)); END LOOP; dbms_output.put_line('******************'); END;

Output

1: 1 2: 4 3: 9 4: 16 5: 25 6: 36 7: 49 8: 64 9: 81 10: 100 ****************** ****************** 10: 100 9: 81 8: 64 7: 49 6: 36 5: 25 4: 16 3: 9 2: 4 1: 1 ******************

Use of Cursor

  1. Cursor
    create or replace PROCEDURE SP_CURSOR_EXAMPLE1 AS /**************************************************************** NAME: SP_CURSOR_EXAMPLE1 PURPOSE: Example for looping records in a cursor. ****************************************************************/ CURSOR c_products IS SELECT PRODUCT_CD FROM TBL_PRODUCT ORDER BY PRODUCT_CD; BEGIN FOR r_proudct IN c_products LOOP dbms_output.put_line(r_proudct.PRODUCT_CD); END LOOP; END;

18 Jan 2013

File Handling using PL/SQL

An Oracle directory is a database object pointing to a operating system directory on the database server machine for reading and writing files.

  1. Create Oracle Directory.
    CREATE OR REPLACE DIRECTORY scott_dir as '/home/scott';
  2. Grant Privileges.
    GRANT read, write ON DIRECTORY scott_dir TO scott;
  3. Read file.
    create or replace PROCEDURE SP_READ_FILE ( as_file_dir IN VARCHAR2, /* Oracle Directory */ as_file_name IN VARCHAR2 /* File Name */ ) AS lh_file_handle      UTL_FILE.FILE_TYPE; ls_line             VARCHAR2(1000); BEGIN lh_file_handle := UTL_FILE.FOPEN(as_file_dir, as_file_name, 'R'); LOOP UTL_FILE.GET_LINE(lh_file_handle,ls_line); dbms_output.put_line(ls_line); END LOOP; UTL_FILE.FCLOSE(lh_file_handle); END;
  4. Write file.
    CREATE OR REPLACE PROCEDURE SP_WRITE_TO_FILE ( as_file_dir VARCHAR2, /* Oracle Directory of the Output Text */ as_file_name VARCHAR2 /* File Name of the Output Text */ ) AS lh_file_handle    UTL_FILE.FILE_TYPE; BEGIN lh_file_handle := UTL_FILE.FOPEN(as_file_dir, as_file_name, 'W', 32000); UTL_FILE.PUT_LINE(lh_file_handle, 'Hello World!!!'); UTL_FILE.FCLOSE(lh_file_handle); EXCEPTION WHEN UTL_FILE.INVALID_OPERATION THEN dbms_output.put_line('Invalid operation Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.INVALID_FILEHANDLE THEN dbms_output.put_line('Invalid File Handler Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.READ_ERROR THEN dbms_output.put_line('Read Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.WRITE_ERROR THEN dbms_output.put_line('Write Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.INVALID_PATH THEN dbms_output.put_line('Invalid Path Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.INVALID_MODE THEN dbms_output.put_line('Invalid Mode Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN UTL_FILE.INTERNAL_ERROR THEN dbms_output.put_line('Internal Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN VALUE_ERROR THEN dbms_output.put_line('Value Error: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); WHEN OTHERS THEN dbms_output.put_line('Other errors: '||SQLCODE||' -ERROR- '||SQLERRM); UTL_FILE.FCLOSE(lh_file_handle); END SP_WRITE_TO_FILE;

17 Jan 2013

FTP in KSH

  1. Get a text file "from.txt" from a FTP server.
    #!/bin/ksh FTP_HOST=ftp.example.com FTP_USER=ftpuser FTP_PASS=pass123 FTP_DIR=/home/ftpuser FTP_FILE=from.txt DEST_DIR=/home/localuser DEST_FILE=to.txt JOB_LOG_FILE=example.log ftp -nv >> $JOB_LOG_FILE << EOFMARK open $FTP_HOST user $FTP_USER $FTP_PASS acsii get $FTP_DIR/$FTP_FILE $DEST_DIR/$DEST_FILE EOFMARK return 0
  2. List the files in a directory from a FTP server.
    #!/bin/ksh FTP_HOST=ftp.example.com FTP_USER=ftpuser FTP_PASS=pass123 FTP_DIR=/home/ftpuser FTP_FILE=from.txt FTP_TMP_FILE=ftp_temp.txt JOB_LOG_FILE=example.log ftp -nv >> $JOB_LOG_FILE << EOFMARK open $FTP_HOST user $FTP_USER $FTP_PASS cd $FTP_DIR prompt off ls * $FTP_TMP_FILE EOFMARK return 0

Executing Oracle statements or Stored Procedures from KSH

  1. To execute an Oracle stored procedure,

    #!/bin/ksh
    USER=scott
    PASS=tiger
    ORACLE_SID=example_sid
    JOB_LOG_FILE=example.log
    sqlplus -s $USER/$PASS"@"$ORACLE_SID >> $JOB_LOG_FILE << EOFMARK
    set serveroutput on
    set timing on
    exec sp_example('ABC');
    exit
    EOFMARK
    return 0
    

    The above script uses SQL Plus as client to connect to Oracle database, and execute the stored procedure "sp_example". All dbms_out.put_line() outputs will be logged into the "example.log" file.

  2. To Run a query on an Oracle database, and output the results to a text file "test.txt",

    #!/bin/ksh

    USER=scott
    PASS=tiger
    ORACLE_SID=example_sid
    JOB_LOG_FILE=example.log
    ORA_TMP_FILE=test.txt

    sqlplus -s $USER/$PASS"@"$ORACLE_SID >> $JOB_LOG_FILE << EOFMARK
    set serveroutput on
    set timing on
    set heading off
    spool $ORA_TMP_FILE
    select TO_CHAR(SYSDATE, 'dd-Mon-yyyy'), COUNT(*) from dual;
    spool off
    EOFMARK


    return 0

    To check whether there is data selected,

    if [[ -e $ORA_TMP_FILE ]];then
      PARAM_INFO=`grep "no rows selected" $ORA_TMP_FILE | wc -l`
    fi
    if [[ $PARAM_INFO -eq 1 ]];then
      echo "No data is selected."
    fi


    To get the data from the text file,

    if [[ -e $ORA_TMP_FILE ]];then
      PARAM_LIST=`cat $ORA_TMP_FILE`
    fi
    if [[ $PARAM_LIST != "" ]];then
      set -A array $PARAM_LIST
      CURRENT_DATE=${array[0]}
      RECORD_COUNT=${array[1]}
      echo "Current date is ${CURRENT_DATE}."
      echo "Record count is ${RECORD_COUNT}."
    fi


    The output is:

    Current date is 17-Jan-2013.
    Record count is 1.

KSH Tips

1. To include a file,

. ./error_handling.sh


2. To check whether a parameter is inputted or not,

if [[ -z $1 ]];then
  echo "Usage: `basename $0` arg1"
  echo "Please provide date in YYYYMMDD as the first parameter when running this script."
  return 100
fi



3. To convert a variable value to upper case,

YOUR_NAME="Mickey Mouse"
typeset -u $YOUR_NAME
echo $YOUR_NAME


The output is:

MICKEY MOUSE


4. To replace words in a file,

sed -e 's/&/&amp;/g' "from.txt" > "to.txt"


5. To trap an error,

JOB_LOG_FILE=example.log
trap 'RC=$?; echo `date` "Error Line No.: $LINENO (Error Code: $RC)" >> $JOB_LOG_FILE' ERR



6. To output the current time in specific format,

echo `date +%y/%m/%d` `date +%H:%M:%S` "Hello World!"

The output is:

Thu Jan 17 16:31:07 HKT 2013 Hello World!


7. To check whether a word appears in a file,

JOB_LOG_FILE=example.log
info=`grep -Ei "error" $JOB_LOG_FILE | wc -l`
if [[ $ftp_info -qt 0 ]];then
  echo "Error(s) found."
else
  echo "No Error found."
fi



8. To execute a Java Jar file,

JOB_LOG_FILE=example.log
JAR_PATH=/home/scott
JAVA_NAMR=example.jar
PARAM_VALUE=test
echo `date` `java -jar $JAR_PATH/$JAR_NAME $PARAM_VALUE` >> $JOB_LOG_FILE



8. To housekeep files, e.g. for 30 days,

KEEP_FILES_IN_DAYS=30
find $LOG_PATH -name "job_*.log" -mtime +${KEEP_FILES_IN_DAYS} -exec rm -f {} \;



Send email with attachments in KSH

  1. Suppose there is an email template with empty body.

    For example,

    to: test_to@example.com cc: test_cc@example.com from: test_from@example.com subject: Test Example with empty body
    To send this email text, for example, named email_tmpl.txt, through sendmail, simple issue the following command:
    sendmail -t < email_tmpl.txt
  2. Suppose there is an email template with plain text body.

    For example,

    to: test_to@example.com cc: test_cc@example.com from: test_from@example.com subject: Test Example with plain text body This is an email with plain text body.

    To send this email text, for example, named email_tmpl.txt, through sendmail, issue the same command in (1).

  3. Suppose there is an email template with plain text body as in (2).  To send with attachments, e.g. attachments "a.html" and "b.html", issue the following command:

    (cat "email_tmpl.txt"; uuencode "a.html" "a.html"; uuencode "b.html" "b.html") | sendmail -t
  4. Suppose there is an email template with HTML body.

    For example,

    to: test_to@example.com cc: test_cc@example.com from: test_from@example.com subject: Test Example with HTML body Content-Type: text/html; charset=utf-8 <html> <head></head> <body>   This is an email template with <b>HTML</b> body! <body> </html>

    To send this email text, use the same command as in (1).

  5. Suppose there is an email template with HTML body.  To send with HTML attachments,

    For example,

    to: test_to@example.com cc: test_cc@example.com from: test_from@example.com subject: Test Example with HTML body Content-Type: multipart/mixed; boundary="test_example_boundary" --test_example_boundary Content-Type: text/html; charset=utf-8 <html> <head></head> <body>   This is an email template with <b>HTML</b> body! <body> </html> --test_example_boundary Content-Type: text/html; charset=utf-8 Content-Disposition: attachment; filename=html_attachment.html <html> <head></head> <body>   This is a <b>HTML</b> attachment! <body> </html> --test_example_boundary--
    To send this email text, use the same command as in (1).