http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/container/util/src/test/resources/spamassassin_db/spam/spam8
----------------------------------------------------------------------
diff --git 
a/server/container/util/src/test/resources/spamassassin_db/spam/spam8 
b/server/container/util/src/test/resources/spamassassin_db/spam/spam8
deleted file mode 100644
index addf463..0000000
--- a/server/container/util/src/test/resources/spamassassin_db/spam/spam8
+++ /dev/null
@@ -1,321 +0,0 @@
-From or...@imail.ru  Sun Jul 15 04:56:31 2001
-Return-Path: <or...@imail.ru>
-Delivered-To: y...@netnoteinc.com
-Received: from cccp.co.kr (unknown [211.218.149.105]) by
-    mail.netnoteinc.com (Postfix) with SMTP id 789D51140BA for
-    <j...@netnoteinc.com>; Sun, 15 Jul 2001 03:56:28 +0000 (Eire)
-Received: from imail.ru [210.14.5.95] by cccp.co.kr running [nMail 1.04
-    (Windows NT/2000) SMTP Server]; Wed, 11 Jul 2001 02:41:35 +0900
-Message-Id: <00000e0836ec$00006b26$00000...@imail.ru>
-To: <6...@163.net>
-From: or...@imail.ru
-Subject: FW:
-Date: Fri, 02 Jan 1998 04:30:44 -0400
-MIME-Version: 1.0
-Content-Transfer-Encoding: quoted-printable
-X-Priority: 3
-X-Msmail-Priority: Normal
-X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0)
-
-
-<HTML>
-<BODY bgColor=3D#C0C0C0>
-
-<FONT face=3D"Arial">
-<FONT size=3D4>
-<FONT color=3D"#800040"> Thank you for your interest!</FONT>
-<FONT size=3D3>
-<FONT color=3D"#800040">  </FONT>
-<FONT color=3D"#000000">                         <BR>
-<BR>
-Judgment Courses offers an extensive Audio training<BR>
-course in </FONT>
-<FONT color=3D"#0000A0"> "How to Collect Money Judgments"</FONT>
-<FONT color=3D"#000000">  .         </FONT>
-<FONT size=3D2>
-<FONT color=3D"#000000"> S6</FONT>
-<FONT size=3D3>
-<FONT color=3D"#000000"> <BR>
-<BR>
-If you are like many people, you are not even sure what a<BR>
-Money Judgment is and </FONT>
-<FONT color=3D"#0000A0"> why processing Money Judgments<BR>
-can earn you very substantial income</FONT>
-<FONT color=3D"#000000">  .<BR>
-<BR>
-If you ever sue a company or a person and you win then you<BR>
-will have a Money Judgment against them.<BR>
-<BR>
-You are happy you won but you will soon find out the<BR>
-shocking fact: "Its now up to you to collect on the<BR>
-Judgment". The court does not require the loser to pay you.<BR>
-The court will not even help you. You must trace the loser<BR>
-down, find their assets, their employment, bank accounts,<BR>
-real estate, stocks and bonds, etc.<BR>
-<BR>
-Very few people know how to find these assets or what to do<BR>
-when they are found. The result is that millions of<BR>
-Judgments are just sitting in files and being forgotten.<BR>
-<BR>
-"In 79% of the cases the winner of a Judgment never sees a<BR>
-dime."<BR>
-<BR>
-The non-payment of judicial debt has grown to epidemic<BR>
-proportions. Right now in the United States there is<BR>
-between </FONT>
-<FONT color=3D"#0000A0"> 200 and 300 billion dollars of uncollected Money<=
-BR>
-Judgment debt</FONT>
-<FONT color=3D"#000000">  . For every Judgment that is paid, 5 more<BR>
-Judgments take its place.<BR>
-<BR>
-We identified this massive market 8 years ago and have<BR>
-actively pursued Judicial Judgments since. We invented this<BR>
-business. We have perfected it into a well proven and solid<BR>
-profession in which only a select few will be trained in the<BR>
-techniques necessary to succeed.<BR>
-<BR>
-With our first hand experience we have built a course which<BR>
-teaches you how to start your business in this new unknown<BR>
-and exciting field of processing Money Judgments.<BR>
-<BR>
-By following the steps laid out in our course and with<BR>
-reasonable effort you can become very successful in the<BR>
-processing of Money Judgments.<BR>
-<BR>
-The income potential is substantial in this profession. </FONT>
-<FONT color=3D"#0000A0"> We<BR>
-have associates who have taken our course and are now<BR>
-working full time making $96,000.00 to over $200,000.00 per<BR>
-year. Part time associates are earning between $24,000.00<BR>
-and $100,000.00 per year </FONT>
-<FONT color=3D"#000000"> . Some choose to operate out of<BR>
-their home and work by themselves. Others build a sizable<BR>
-organization of 15 to 25 people in attractive business<BR>
-offices.<BR>
-<BR>
-Today our company and our associates have over 126<BR>
-million dollars in Money Judgments that we are currently<BR>
-processing. Of this 126 million, 25 million is in the form<BR>
-of joint ventures between our firm and our associates.<BR>
-Joint ventures are where we make our money. We only break<BR>
-even when our course is purchased. We make a 12% margin on<BR>
-the reports we supply to our associates. Our reporting<BR>
-capability is so extensive that government agencies, police<BR>
-officers, attorneys, credit agencies etc., all come to us<BR>
-for reports.<BR>
-<BR>
-<BR>
-Many of our associates already have real estate liens in<BR>
-force of between 5 million to over 15 million dollars.<BR>
-Legally this means that when the properties are sold or<BR>
-refinanced our associate must be paid off. The norm is 10%<BR>
-interest compounded annually on unpaid Money Judgments.<BR>
-Annual interest on 5 million at 10% translates to<BR>
-$500,000.00 annually in interest income, not counting the<BR>
-payment of the principal.<BR>
-<BR>
-Our associates earn half of this amount or $250,000.00 per<BR>
-year. This is just for interest, not counting principle<BR>
-and not counting the compounding of the interest which can<BR>
-add substantial additional income. Typically companies are<BR>
-sold for 10 times earnings. Just based on simple interest<BR>
-an associate with 5 million in real estate liens could sell<BR>
-their business for approximately 2.5 million dollars.<BR>
-<BR>
-</FONT>
-<FONT color=3D"#0000A0"> 92% of all of our associates work out of their ho=
-me; 43%<BR>
-are women and 36% are part time</FONT>
-<FONT color=3D"#000000">  .<BR>
-<BR>
-One of the benefits of working in this field is that you are<BR>
-not under any kind of time frame. If you decide to take off<BR>
-for a month on vacation then go. The Judgments you are<BR>
-working on will be there when you return. The Judgments<BR>
-are still in force, they do not disappear.<BR>
-<BR>
-The way we train you is non-confrontational. You use your<BR>
-computer and telephone to do most of the processing. You<BR>
-never confront the debtor. The debtor doesn't know who you<BR>
-are. You are not a collection agency.<BR>
-<BR>
-Simply stated the steps to successful Money Processing<BR>
-are as follows:<BR>
-<BR>
-Mail our recommended letter to companies and individuals<BR>
-with Money Judgments. (We train you how to find out who<BR>
-to write to)<BR>
-<BR>
-8% to 11% of the firms and people you write will call you<BR>
-and ask for your help. They call you, you don't call them<BR>
-unless you want to.<BR>
-<BR>
-You send them an agreement (supplied in the course) to<BR>
-sign which splits every dollar you collect 50% to you and<BR>
-50% to them. This applies no matter if the judgment is for<BR>
-$2,000.00 or $2,000,000.00.<BR>
-<BR>
-You then go on-line to our computers to find the debtor<BR>
-and their assets. We offer over 120 powerful reports to<BR>
-assist you. They range from credit reports from all three<BR>
-credit bureaus, to bank account locates, employment<BR>
-locates, skip traces and locating stocks and bonds, etc.<BR>
-The prices of our reports are very low. Typically 1/2 to<BR>
-1/3 of what other firms charge. For example we charge<BR>
-$6.00 for an individuals credit report when some other<BR>
-companies charge $25.00.<BR>
-<BR>
-Once you find the debtor and their assets you file<BR>
-garnishments and liens on the assets you have located.<BR>
-(Standard fill in the blanks forms are included in the<BR>
-course)<BR>
-<BR>
-When you receive the assets you keep 50% and send 50% to<BR>
-the original Judgment holder.<BR>
-<BR>
-Once the Judgment is fully paid you mail a Satisfaction of<BR>
-Judgment to the court. (Included in the course)<BR>
-<BR>
-Quote's from several of our students:<BR>
-<BR>
-Thomas in area code 516 writes us: "I just wanted to drop<BR>
-you a short note thanking you for your excellent course. </FONT>
-<FONT color=3D"#0000A0"> My<BR>
-first week, part time, will net me 3,700.00 dollars</FONT>
-<FONT color=3D"#000000">  . Your<BR>
-professionalism in both the manual and the video opened<BR>
-doors for me in the future. There's no stopping me now.<BR>
-Recently Thomas states he has over $8,500,000 worth of<BR>
-judgments he is working on.<BR>
-<BR>
-After only having this course for four months, Larry S. in<BR>
-area code 314 stated to us: " </FONT>
-<FONT color=3D"#0000A0"> I am now making $2,000.00 per<BR>
-week </FONT>
-<FONT color=3D"#000000"> and expect this to grow to twice this amount with=
-in the<BR>
-next year. I am having a ball. I have over $250,000 in<BR>
-judgments I am collecting on now."<BR>
-<BR>
-After having our course for 7 months Larry S. in 314 stated<BR>
-" </FONT>
-<FONT color=3D"#0000A0"> I am now making $12,000.00</FONT>
-<FONT color=3D"#000000">  per month and have approximately<BR>
-$500,000.00 in judgments I am collecting on. Looks like I<BR>
-will have to hire someone to help out"<BR>
-<BR>
-Marshal in area code 407 states to us "I feel bad, you only<BR>
-charged me $259.00 for this course and it is a goldmine. I<BR>
-have added 3 full time people to help me after only having<BR>
-your course for 5 months"<BR>
-<BR>
->From the above information and actual results you can see<BR>
-why we can state the following:<BR>
-<BR>
-With our course you can own your own successful business.<BR>
-A business which earns you substantial income now and one<BR>
-which could be sold in 3-5 years, paying you enough to<BR>
-retire on and travel the world. A business which is<BR>
-extremely interesting to be in. A Business in which every<BR>
-day is new and exciting.<BR>
-<BR>
-None of your days will be hum-drum. Your brain is<BR>
-Challenged. A business, which protects you from Corporate<BR>
-Downsizing. A business which you can start part time from<BR>
-your home and later, if you so desire, you can work in full<BR>
-time. A business, which is your ticket to freedom from<BR>
-others telling you what to do. A business, which lets you<BR>
-control your own destiny. Our training has made this happen<BR>
-for many others already. Make it happen for you!<BR>
-<BR>
-If the above sounds interesting to you then its time for you<BR>
-to talk to a real live human being, no cost or obligation<BR>
-on your part.<BR>
-<BR>
-</FONT>
-<FONT color=3D"#800040"> Please call us at 1_406_652_0194 </FONT>
-<FONT color=3D"#000000"> .<BR>
-<BR>
-We have </FONT>
-<FONT color=3D"#800040"> Customer Support staff available to you from 8:00=
-am to<BR>
-9:00pm (Mountain Time) 7 days a week</FONT>
-<FONT color=3D"#000000">  . If you call this number<BR>
-you can talk to one of our experienced Customer Support personnel.<BR>
-They can answer any questions you may have - with no obligation.<BR>
-Sometimes we run special pricing on our courses and combinations<BR>
-of courses. When you call our Customer Support line they can let<BR>
-you know of any specials we may be running. If you like what you<BR>
-read and hear about our courses, then the Customer Support person<BR>
-can work with you to place your order. We are very low key. We<BR>
-merely give you the facts and you can then decide if you want to<BR>
-work with us or not.<BR>
-<BR>
-Thank you for your time and interest.<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-<BR>
-</FONT>
-<FONT face=3D"Courier New">
-<FONT size=3D2>
-<FONT color=3D"#000000"> .................................................=
-....................<BR>
-T h i s message i s produced a n d sent out by:<BR>
-Universal S.y.s.t.e.m.s.<BR>
-To be dropped form our mailing list please email us at <BR>
-jame...@mail.asia-links.com call us toll free at 1=3D888=3D605=3D2485<BR>
-and  g i v e us y o u r email a d d r e s s  or w r i t e  us a t:<BR>
-*Central*DB*Processing, PO: Box:1200, O r a n j e s t a d, Aruba<BR>
-.....................................................................<BR>
-</FONT>
-<FONT face=3D"Arial">
-<FONT size=3D3>
-<FONT color=3D"#000000"> <BR>
-<BR>
-</FONT>
-<FONT size=3D3>
-<FONT color=3D"#000000"> <BR>
-<BR>
-</FONT>
-<FONT size=3D3>
-<FONT color=3D"#000000"> <BR>
-<BR>
-<BR>
-<BR>
-<BR>
-</FONT>
-<FONT size=3D2>
-<FONT color=3D"#000000"> <BR>
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=
-'''''''''''<BR>
-</FONT>
-<FONT size=3D3>
-<FONT color=3D"#000000"> <BR>
-</FONT></FONT></FONT></FONT></FONT></FONT></FONT></FONT></FONT></FONT></FO=
-NT></FONT></FONT></BODY></HTML>
-
-
-
-

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/container/util/src/test/resources/spamassassin_db/spam/spam9
----------------------------------------------------------------------
diff --git 
a/server/container/util/src/test/resources/spamassassin_db/spam/spam9 
b/server/container/util/src/test/resources/spamassassin_db/spam/spam9
deleted file mode 100644
index 163bde8..0000000
--- a/server/container/util/src/test/resources/spamassassin_db/spam/spam9
+++ /dev/null
@@ -1,142 +0,0 @@
-From spamassassin-sightings-ad...@lists.sourceforge.net  Tue Jul  2 13:04:23 
2002
-Return-Path: <spamassassin-sightings-ad...@example.sourceforge.net>
-Delivered-To: y...@localhost.labs.netnoteinc.com
-Received: from localhost (localhost [127.0.0.1])
-       by phobos.labs.netnoteinc.com (Postfix on SuSE Linux 8.0 (i386)) with 
ESMTP id BB16E14F914
-       for <jm@localhost>; Tue,  2 Jul 2002 12:56:51 +0100 (IST)
-Received: from dogma.slashnull.org [212.17.35.15]
-       by localhost with IMAP (fetchmail-5.9.0)
-       for jm@localhost (single-drop); Tue, 02 Jul 2002 12:56:51 +0100 (IST)
-Received: from usw-sf-list2.sourceforge.net (usw-sf-fw2.sourceforge.net
-    [216.136.171.252]) by dogma.slashnull.org (8.11.6/8.11.6) with ESMTP id
-    g61Iat611966 for <jm-sa-sighti...@jmason.org>; Mon, 1 Jul 2002 19:36:56
-    +0100
-Received: from usw-sf-list1-b.sourceforge.net ([10.3.1.13]
-    helo=usw-sf-list1.sourceforge.net) by usw-sf-list2.sourceforge.net with
-    esmtp (Exim 3.31-VA-mm2 #1 (Debian)) id 17P62B-0000rw-00; Mon,
-    01 Jul 2002 11:36:23 -0700
-Received: from [64.86.155.148] (helo=ok61655.com) by
-    usw-sf-list1.sourceforge.net with smtp (Exim 3.31-VA-mm2 #1 (Debian)) id
-    17P60P-0006ds-00 for <spamassassin-sighti...@lists.sourceforge.net>;
-    Mon, 01 Jul 2002 11:34:34 -0700
-From: "MR.DOUGLAS  AND PRINCESS M." <douglassmith2...@yahoo.co.uk>
-Reply-To: princessmar...@yahoo.com
-To: spamassassin-sighti...@example.sourceforge.net
-X-Mailer: Microsoft Outlook Express 5.00.2919.6900 DM
-MIME-Version: 1.0
-Message-Id: <e17p60p-0006ds...@usw-sf-list1.sourceforge.net>
-Subject: [SA] URGENT HELP..............
-Sender: spamassassin-sightings-ad...@example.sourceforge.net
-Errors-To: spamassassin-sightings-ad...@example.sourceforge.net
-X-Beenthere: spamassassin-sighti...@example.sourceforge.net
-X-Mailman-Version: 2.0.9-sf.net
-Precedence: bulk
-List-Help: 
<mailto:spamassassin-sightings-requ...@example.sourceforge.net?subject=help>
-List-Post: <mailto:spamassassin-sighti...@example.sourceforge.net>
-List-Subscribe: 
<https://example.sourceforge.net/lists/listinfo/spamassassin-sightings>,
-    
<mailto:spamassassin-sightings-requ...@lists.sourceforge.net?subject=subscribe>
-List-Id: <spamassassin-sightings.example.sourceforge.net>
-List-Unsubscribe: 
<https://example.sourceforge.net/lists/listinfo/spamassassin-sightings>,
-    
<mailto:spamassassin-sightings-requ...@lists.sourceforge.net?subject=unsubscribe>
-List-Archive: 
<http://www.geocrawler.com/redir-sf.php3?list=spamassassin-sightings>
-X-Original-Date: Mon, 5 Apr 1999 20:38:02 +0100
-Date: Mon, 5 Apr 1999 20:38:02 +0100
-Content-Type: multipart/mixed; boundary="===_SecAtt_000_1fnapngoonxcrm"
-
---===_SecAtt_000_1fnapngoonxcrm
-Content-Type: text/plain; charset="us-ascii"
-Content-Transfer-Encoding: quoted-printable
-
-
-DEAR SIR=2C
-URGENT AND CONFIDENTIAL=3A
-
-Re=3ATransfer of $50=2C000=2E000=2E00 USD=5BFIFTY MILLION UNITED
-STATES DOLLARS=5D=2E
-
-WE WANT TO TRANSFER TO OVERSEAS=5B$50=2C000=2E000=2E00=5BFIFTY
-MILLION UNITED STATES DOLLARS=5DFROM A SECURITY COMPANY
-IN SPAIN=2CI WANT TO ASK YOU TO QUIETLY LOOK FOR A
-RELIABLE AND HONEST PERSON WHO WILL BE CAPABLE AND FIT
-TO PROVIDE EITHER AN EXISTING BANK ACCOUNT OR TO SET
-UP A NEW BANK ACCOUNT IMMEDIATELY TO RECEIVE THIS
-MONEY=2CEVEN AN EMPTY ACCOUNT CAN SERVE TO RECEIVE THIS
-MONEY=2CAS LONG AS YOU WILL REMAIN HONEST TO ME TILL THE
-END OF THIS IMPORTANT BUSINESS TRANSACTION=2EI WANT TO
-BELIEVE THAT YOU WILL NEVER LET ME DOWN EITHER NOW OR
-IN FUTURE=2E
-
-I AM DOUGLAS SMITH=2CTHE AUDITOR GENERAL OF MAGNUM TRUST
-INC=2E MADRID SPAIN DURING THE COURSE OF OUR AUDITING=2CI DISCOVERED A
-FLOATING FUND IN AN ACCOUNT OPENED IN THE SECURITY
-COMPANY IN 1990 AND SINCE 1993 NOBODY HAS OPERATED ON
-THIS ACCOUNT AGAIN=2C AFTER GOING THROUGH SOME OLD FILES
-IN THE RECORDS I DISCOVERED THAT THE OWNER OF THE
-ACCOUNT DIED WITHOUT A HEIR HENCE THE MONEY IS
-FLOATING AND IF I DO NOT REMIT THIS MONEY OUT
-URGENTLY=2CIT WILL BE FORFEITED FOR NOTHING=2E
-
-THE OWNER OF THIS ACCOUNT WAS =22MR=2EALLAN P=2E SEAMAN=22=2CA FOREIGNER AND 
AN INDUSTRIALIST=2CAND HE DIED SINCE 1993 AND NO OTHER PERSON KNOWS ABOUT THIS 
ACCOUNT OR ANYTHING CONCERNING IT=2CTHE ACCOUNT HAS NO BENEFICIARY AND MY 
INVESTIGATION PROVED TO ME AS WELL THAT ALLAN=2EP SEAMAN UNTILL HIS DEATH WAS 
THE MANAGER DIAMOND SAFARI=5BPTY=5DS=2EA=2E
-
-WE WILL START THE FIRST TRANSFER WITH $20 MILLION
-DOLLARS=5BTWENTY MILLION DOLLARS=5DUPON SUCCESSFUL
-TRANSFER WITHOUT ANY DISAPPOINTMENT FROM YOUR SIDE=2CWE
-SHALL RE-APPLY FOR THE TRANSFER OF THE REMAINING $30
-MILLION USA DOLLARS=5BTHIRTY MILLION UNITED STATES
-DOLLARS=5DINTO YOUR ACCOUNT BRINGING THE SUM TOTAL TO
-$50=2C000=2E000=2E00=5BFIFTY MILLION DOLLARS=5D=2E
-
-I AM CONTACTING YOU AS A FOREIGNER BECAUSE THIS MONEY
-CAN ONLY BE APPROVED TO A FOREIGNER WITH VALID
-INTERNATIONAL PASSPORT=2CDRIVERS LICENCE AND FOREIGN
-ACCOUNT BECAUSE THE MONEY IS IN USA DOLLARS AND THE
-FORMER OWNER OF THE ACCOUNT=22MR=2EALLAN P=2ESEAMAN IS A
-FOREIGNER=2EI WILL LIKE US TO MEET FACE TO FACE TO SIGN
-A BINDING AGREEMENT THAT WILL BIND US TOGETHER IN THE
-BUSINESS=2CI AM REVEALING ALL THESE TO YOU WITH THE
-BELIEF THAT YOU WILL NEVER LET ME DOWN IN THIS
-BUSINESS=2CYOU ARE THE FIRST AND ONLY PERSON I AM
-CONTACTING FOR THE BUSINESS=2CSO PLEASE REPLY URGENTLY
-FOR ME TO TELL YOU THE NEXT STEP TO TAKE=2EYOU SHOULD
-FORWARD YOUR TELEPHONE AND FAX NUMBERS AND YOUR BANK
-ACCOUNT DETAILS THAT WILL BE USED IN TRANSFERING THE
-MONEY=2E
-
-YOU WILL HAVE TO GIVE ME THE ASSURANCE WHEN WE
-MEET THAT THIS MONEY WILL BE INTACT PENDING OUR
-PHYSICAL ARRIVAL IN YOUR COUNTRY FOR SHARING AND
-DISBURSEMENT OF THE FUND WHICH WILL BE 35% FOR YOUR
-ASSISTANCE=2C60% WILL BE FOR US WHILE 5% WILL  BE SET
-ASIDE TO TAKE CARE OF ALL THE EXPENSES THAT WILL BE
-INCURRED BY BOTH PARTIES DURING THE COURSE OF THE
-TRANSFER=2E
-
-I LOOK FORWARD TO YOUR EARLIEST RESPONSE=2CALL
-CORRESPONDENCE FOR NOW SHOULD BE EMAIL FOR SECURITY
-REASONS=2E
-
-BEST REGARDS=2E
-
-DOUGLAS SMITH  =28PLEASE  CONTACT ME THROUGH  MY WIFE 
EMAIL=28princessmar001=40yahoo=2Ecom=29FOR CONFIDENCAIL
-
-
-
---===_SecAtt_000_1fnapngoonxcrm
-Content-Type: application/octet-stream; name="aaaaaaa.txt"
-Content-Transfer-Encoding: base64
-Content-Disposition: attachment; filename="aaaaaaa.txt"
-
-
---===_SecAtt_000_1fnapngoonxcrm
-
-
-
--------------------------------------------------------
-This sf.net email is sponsored by:ThinkGeek
-Welcome to geek heaven.
-http://thinkgeek.com/sf
-_______________________________________________
-Spamassassin-Sightings mailing list
-spamassassin-sighti...@lists.sourceforge.net
-https://lists.sourceforge.net/lists/listinfo/spamassassin-sightings
-

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
 
b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
index cad6813..1f33b14 100644
--- 
a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
+++ 
b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
@@ -38,10 +38,10 @@ import 
org.apache.james.mailets.configuration.CommonProcessors;
 import org.apache.james.mailets.configuration.MailetConfiguration;
 import org.apache.james.mailets.configuration.MailetContainer;
 import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.spamassassin.SpamAssassinResult;
 import org.apache.james.transport.matchers.All;
 import org.apache.james.util.docker.Images;
 import org.apache.james.util.docker.SwarmGenericContainer;
-import org.apache.james.util.scanner.SpamAssassinResult;
 import org.apache.james.utils.DataProbeImpl;
 import org.apache.james.utils.IMAPMessageReader;
 import org.apache.james.utils.SMTPMessageSender;

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/mailet/mailets/pom.xml
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/pom.xml b/server/mailet/mailets/pom.xml
index 5c2dd1e..9e65f80 100644
--- a/server/mailet/mailets/pom.xml
+++ b/server/mailet/mailets/pom.xml
@@ -67,6 +67,16 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-spamassassin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-spamassassin</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>apache-mailet-api</artifactId>
             <scope>compile</scope>
         </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java
index e704497..f9f2634 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java
@@ -27,11 +27,11 @@ import javax.mail.internet.MimeMessage;
 
 import org.apache.james.core.MailAddress;
 import org.apache.james.metrics.api.MetricFactory;
+import org.apache.james.spamassassin.SpamAssassinInvoker;
+import org.apache.james.spamassassin.SpamAssassinResult;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
 import org.apache.james.util.Port;
-import org.apache.james.util.scanner.SpamAssassinInvoker;
-import org.apache.james.util.scanner.SpamAssassinResult;
 import org.apache.mailet.Mail;
 import org.apache.mailet.PerRecipientHeaders;
 import org.apache.mailet.base.GenericMailet;

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/IsMarkedAsSpam.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/IsMarkedAsSpam.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/IsMarkedAsSpam.java
index 596d2b7..bd516f5 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/IsMarkedAsSpam.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/IsMarkedAsSpam.java
@@ -25,7 +25,7 @@ import java.util.Locale;
 import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
-import org.apache.james.util.scanner.SpamAssassinResult;
+import org.apache.james.spamassassin.SpamAssassinResult;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.GenericMatcher;
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
 
b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
index 81f9674..62565ab 100644
--- 
a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
+++ 
b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java
@@ -27,11 +27,11 @@ import javax.mail.MessagingException;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.builder.MimeMessageBuilder;
 import org.apache.james.metrics.api.NoopMetricFactory;
+import org.apache.james.spamassassin.SpamAssassinResult;
+import org.apache.james.spamassassin.mock.MockSpamd;
+import org.apache.james.spamassassin.mock.MockSpamdTestRule;
 import org.apache.james.user.memory.MemoryUsersRepository;
 import org.apache.james.util.Port;
-import org.apache.james.util.scanner.SpamAssassinResult;
-import org.apache.james.utils.MockSpamd;
-import org.apache.james.utils.MockSpamdTestRule;
 import org.apache.mailet.Mail;
 import org.apache.mailet.PerRecipientHeaders;
 import org.apache.mailet.base.test.FakeMail;

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
index 986f7db..6309274 100644
--- 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
+++ 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
@@ -110,6 +110,12 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-spamassassin</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-testing</artifactId>
             <scope>test</scope>
         </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraJmapExtension.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraJmapExtension.java
 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraJmapExtension.java
index 3adc452..4c20a0f 100644
--- 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraJmapExtension.java
+++ 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraJmapExtension.java
@@ -33,8 +33,8 @@ import org.apache.james.modules.TestESMetricReporterModule;
 import org.apache.james.modules.TestElasticSearchModule;
 import org.apache.james.modules.TestJMAPServerModule;
 import org.apache.james.server.core.configuration.Configuration;
+import org.apache.james.spamassassin.SpamAssassinExtension;
 import org.apache.james.util.Runnables;
-import org.apache.james.util.scanner.SpamAssassinExtension;
 import org.junit.jupiter.api.extension.AfterAllCallback;
 import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeAllCallback;

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
index 0adc13f..7f69759 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
@@ -83,6 +83,12 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-spamassassin</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-testing</artifactId>
             <scope>test</scope>
         </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/JamesWithSpamAssassin.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/JamesWithSpamAssassin.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/JamesWithSpamAssassin.java
index cedde75..6a10a29 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/JamesWithSpamAssassin.java
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/JamesWithSpamAssassin.java
@@ -19,7 +19,7 @@
 package org.apache.james.jmap.methods.integration;
 
 import org.apache.james.GuiceJamesServer;
-import org.apache.james.util.scanner.SpamAssassinExtension;
+import org.apache.james.spamassassin.SpamAssassinExtension;
 
 public class JamesWithSpamAssassin {
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinModule.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinModule.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinModule.java
index ef1ea07..6f8b6d9 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinModule.java
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinModule.java
@@ -25,9 +25,9 @@ import javax.inject.Singleton;
 import org.apache.commons.configuration.BaseConfiguration;
 import org.apache.james.mailbox.spamassassin.SpamAssassinConfiguration;
 import org.apache.james.mailetcontainer.impl.MailetConfigImpl;
+import org.apache.james.spamassassin.SpamAssassinExtension;
+import org.apache.james.spamassassin.SpamAssassinExtension.SpamAssassin;
 import org.apache.james.util.Host;
-import org.apache.james.util.scanner.SpamAssassinExtension;
-import org.apache.james.util.scanner.SpamAssassinExtension.SpamAssassin;
 import org.apache.james.utils.MailetConfigurationOverride;
 
 import com.google.inject.AbstractModule;

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
index db2c09c..c873e5e 100644
--- 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
+++ 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
@@ -74,6 +74,12 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-spamassassin</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-memory-guice</artifactId>
             <type>test-jar</type>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemoryJmapExtension.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemoryJmapExtension.java
 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemoryJmapExtension.java
index 380221f..7ff67b6 100644
--- 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemoryJmapExtension.java
+++ 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemoryJmapExtension.java
@@ -32,7 +32,7 @@ import org.apache.james.mailbox.store.search.PDFTextExtractor;
 import org.apache.james.mailbox.store.search.SimpleMessageSearchIndex;
 import org.apache.james.modules.TestJMAPServerModule;
 import org.apache.james.server.core.configuration.Configuration;
-import org.apache.james.util.scanner.SpamAssassinExtension;
+import org.apache.james.spamassassin.SpamAssassinExtension;
 import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/protocols-smtp/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/protocols-smtp/pom.xml 
b/server/protocols/protocols-smtp/pom.xml
index eec9266..1be572d 100644
--- a/server/protocols/protocols-smtp/pom.xml
+++ b/server/protocols/protocols-smtp/pom.xml
@@ -34,6 +34,16 @@
 
     <dependencies>
         <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-spamassassin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-spamassassin</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>${javax.activation.groupId}</groupId>
             <artifactId>${javax.activation.artifactId}</artifactId>
             <scope>provided</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/SpamAssassinHandler.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/SpamAssassinHandler.java
 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/SpamAssassinHandler.java
index 3a274a8..ef60371 100644
--- 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/SpamAssassinHandler.java
+++ 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/fastfail/SpamAssassinHandler.java
@@ -33,8 +33,8 @@ import org.apache.james.protocols.smtp.dsn.DSNStatus;
 import org.apache.james.protocols.smtp.hook.HookResult;
 import org.apache.james.protocols.smtp.hook.HookReturnCode;
 import org.apache.james.smtpserver.JamesMessageHook;
-import org.apache.james.util.scanner.SpamAssassinInvoker;
-import org.apache.james.util.scanner.SpamAssassinResult;
+import org.apache.james.spamassassin.SpamAssassinInvoker;
+import org.apache.james.spamassassin.SpamAssassinResult;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SpamAssassinHandlerTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SpamAssassinHandlerTest.java
 
b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SpamAssassinHandlerTest.java
index 97acaea..005bb00 100644
--- 
a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SpamAssassinHandlerTest.java
+++ 
b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SpamAssassinHandlerTest.java
@@ -33,9 +33,9 @@ import org.apache.james.protocols.smtp.hook.HookResult;
 import org.apache.james.protocols.smtp.hook.HookReturnCode;
 import org.apache.james.protocols.smtp.utils.BaseFakeSMTPSession;
 import org.apache.james.smtpserver.fastfail.SpamAssassinHandler;
-import org.apache.james.util.scanner.SpamAssassinResult;
-import org.apache.james.utils.MockSpamd;
-import org.apache.james.utils.MockSpamdTestRule;
+import org.apache.james.spamassassin.SpamAssassinResult;
+import org.apache.james.spamassassin.mock.MockSpamd;
+import org.apache.james.spamassassin.mock.MockSpamdTestRule;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.test.FakeMail;
 import org.junit.Rule;

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/testing/src/main/java/org/apache/james/utils/MockSpamd.java
----------------------------------------------------------------------
diff --git a/server/testing/src/main/java/org/apache/james/utils/MockSpamd.java 
b/server/testing/src/main/java/org/apache/james/utils/MockSpamd.java
deleted file mode 100644
index 45121c7..0000000
--- a/server/testing/src/main/java/org/apache/james/utils/MockSpamd.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-package org.apache.james.utils;
-
-import static org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.nio.charset.StandardCharsets;
-
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-
-/**
- * This class can be used to run a mocked SPAMD daemon
- */
-public class MockSpamd implements Runnable {
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(MockSpamd.class);
-
-    /**
-     * Mailcontent which is 100% spam
-     */
-    public static final String GTUBE = "-SPAM-";
-    public static final String NOT_SPAM = "Spam: False ; 3 / 5";
-    public static final String SPAM = "Spam: True ; 1000 / 5";
-
-    private ServerSocket socket;
-    private boolean isBinded;
-
-    public MockSpamd() {
-        isBinded = false;
-    }
-
-    public int getPort() {
-        Preconditions.checkState(isBinded, "SpamD mock server is not binded");
-        return socket.getLocalPort();
-    }
-
-    public void bind() throws IOException {
-        socket = new ServerSocket(0);
-        isBinded = true;
-    }
-
-    @Override
-    public void run() {
-        try (ServerSocket serverSocket = socket;
-            Socket spamd = serverSocket.accept();
-             BufferedReader in = new BufferedReader(new 
InputStreamReader(spamd.getInputStream()));
-             OutputStream out = spamd.getOutputStream()) {
-
-            handleRequest(in, out);
-        } catch (IOException e) {
-            LOGGER.error("Exception while handling answer", e);
-        }
-    }
-
-    private void handleRequest(BufferedReader in, OutputStream out) throws 
IOException {
-        if (isSpam(in)) {
-            out.write(SPAM.getBytes());
-        } else {
-            out.write(NOT_SPAM.getBytes());
-        }
-        out.flush();
-    }
-
-    private boolean isSpam(BufferedReader in) throws IOException {
-        try {
-            return in.lines()
-                .anyMatch(line -> line.contains(GTUBE));
-        } finally {
-            consume(in);
-        }
-    }
-
-    private void consume(BufferedReader in) throws IOException {
-        IOUtils.copy(in, NULL_OUTPUT_STREAM, StandardCharsets.UTF_8);
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/server/testing/src/main/java/org/apache/james/utils/MockSpamdTestRule.java
----------------------------------------------------------------------
diff --git 
a/server/testing/src/main/java/org/apache/james/utils/MockSpamdTestRule.java 
b/server/testing/src/main/java/org/apache/james/utils/MockSpamdTestRule.java
deleted file mode 100644
index ea746df..0000000
--- a/server/testing/src/main/java/org/apache/james/utils/MockSpamdTestRule.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.utils;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import org.junit.rules.ExternalResource;
-
-public class MockSpamdTestRule extends ExternalResource {
-
-    private ExecutorService executor = Executors.newSingleThreadExecutor();
-    private MockSpamd spamd = new MockSpamd();
-
-    @Override
-    protected void before() throws Throwable {
-        spamd.bind();
-        executor.execute(spamd);
-    }
-
-    public int getPort() {
-        return spamd.getPort();
-    }
-
-    @Override
-    protected void after() {
-        executor.shutdownNow();
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/third-party/pom.xml
----------------------------------------------------------------------
diff --git a/third-party/pom.xml b/third-party/pom.xml
new file mode 100644
index 0000000..0ee2019
--- /dev/null
+++ b/third-party/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.james</groupId>
+        <artifactId>james-project</artifactId>
+        <version>3.2.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>third-party</artifactId>
+    <packaging>pom</packaging>
+
+    <name>Apache JAMES third party</name>
+    <description>Common utilities for integrating with third party 
software</description>
+    <inceptionYear>2018</inceptionYear>
+
+    <modules>
+        <module>spamassassin</module>
+    </modules>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/third-party/spamassassin/pom.xml
----------------------------------------------------------------------
diff --git a/third-party/spamassassin/pom.xml b/third-party/spamassassin/pom.xml
new file mode 100644
index 0000000..4b1da22
--- /dev/null
+++ b/third-party/spamassassin/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.james</groupId>
+        <artifactId>third-party</artifactId>
+        <version>3.2.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>apache-james-spamassassin</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>javax-mail-extension</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>metrics-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.fge</groupId>
+            <artifactId>throwing-lambdas</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>javax.mail</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinInvoker.java
----------------------------------------------------------------------
diff --git 
a/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinInvoker.java
 
b/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinInvoker.java
new file mode 100644
index 0000000..2a9fd8a
--- /dev/null
+++ 
b/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinInvoker.java
@@ -0,0 +1,251 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.spamassassin;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.metrics.api.MetricFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.Throwing;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+/**
+ * Sends the message through daemonized SpamAssassin (spamd), visit <a
+ * href="SpamAssassin.org">SpamAssassin.org</a> for info on configuration.
+ */
+public class SpamAssassinInvoker {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(SpamAssassinInvoker.class);
+
+    enum MessageClass {
+        HAM("ham"),
+        SPAM("spam");
+
+        private final String value;
+
+        MessageClass(String value) {
+            this.value = value;
+        }
+    }
+
+    private static final int SPAM_INDEX = 1;
+    private static final int HITS_INDEX = 3;
+    private static final int REQUIRED_HITS_INDEX = 5;
+    private static final String CRLF = "\r\n";
+
+    private final MetricFactory metricFactory;
+    private final String spamdHost;
+    private final int spamdPort;
+
+    /**
+     * Init the spamassassin invoker
+     *
+     * @param spamdHost
+     *            The host on which spamd runs
+     * @param spamdPort
+     */
+    public SpamAssassinInvoker(MetricFactory metricFactory, String spamdHost, 
int spamdPort) {
+        this.metricFactory = metricFactory;
+        this.spamdHost = spamdHost;
+        this.spamdPort = spamdPort;
+    }
+
+    /**
+     * Scan a MimeMessage for spam by passing it to spamd.
+     * 
+     * @param message
+     *            The MimeMessage to scan
+     * @return true if spam otherwise false
+     * @throws MessagingException
+     *             if an error on scanning is detected
+     */
+    public SpamAssassinResult scanMail(MimeMessage message, String user) 
throws MessagingException {
+        return metricFactory.withMetric(
+            "spamAssassin-check",
+            Throwing.supplier(
+                () -> scanMailWithAdditionalHeaders(message,
+                    "User: " + user))
+                .sneakyThrow());
+    }
+
+    public SpamAssassinResult scanMail(MimeMessage message) throws 
MessagingException {
+        return metricFactory.withMetric(
+            "spamAssassin-check",
+            Throwing.supplier(
+                () -> scanMailWithoutAdditionalHeaders(message))
+            .sneakyThrow());
+    }
+
+    public SpamAssassinResult scanMailWithAdditionalHeaders(MimeMessage 
message, String... additionalHeaders) throws MessagingException {
+        try (Socket socket = new Socket(spamdHost, spamdPort);
+             OutputStream out = socket.getOutputStream();
+             BufferedOutputStream bufferedOutputStream = new 
BufferedOutputStream(out);
+             PrintWriter writer = new PrintWriter(bufferedOutputStream);
+             BufferedReader in = new BufferedReader(new 
InputStreamReader(socket.getInputStream()))) {
+
+            writer.write("CHECK SPAMC/1.2");
+            writer.write(CRLF);
+
+            Arrays.stream(additionalHeaders)
+                .forEach(header -> {
+                    writer.write(header);
+                    writer.write(CRLF);
+                });
+
+            writer.write(CRLF);
+            writer.flush();
+
+            // pass the message to spamd
+            message.writeTo(out);
+            out.flush();
+            socket.shutdownOutput();
+
+            return in.lines()
+                .filter(this::isSpam)
+                .map(this::processSpam)
+                .findFirst()
+                .orElse(SpamAssassinResult.empty());
+        } catch (UnknownHostException e) {
+            throw new MessagingException("Error communicating with spamd. 
Unknown host: " + spamdHost);
+        } catch (IOException | MessagingException e) {
+            throw new MessagingException("Error communicating with spamd on " 
+ spamdHost + ":" + spamdPort, e);
+        }
+    }
+
+    public SpamAssassinResult scanMailWithoutAdditionalHeaders(MimeMessage 
message) throws MessagingException {
+        return scanMailWithAdditionalHeaders(message);
+    }
+
+    private SpamAssassinResult processSpam(String line) {
+        List<String> elements = Lists.newArrayList(Splitter.on(' 
').split(line));
+
+        return builderFrom(elements)
+            .hits(elements.get(HITS_INDEX))
+            .requiredHits(elements.get(REQUIRED_HITS_INDEX))
+            .build();
+    }
+
+    private SpamAssassinResult.Builder builderFrom(List<String> elements) {
+        if (spam(elements.get(SPAM_INDEX))) {
+            return SpamAssassinResult.asSpam();
+        } else {
+            return SpamAssassinResult.asHam();
+        }
+    }
+
+    private boolean spam(String string) {
+        try {
+            return Boolean.valueOf(string);
+        } catch (Exception e) {
+            LOGGER.warn("Fail parsing spamassassin answer: " + string);
+            return false;
+        }
+    }
+
+    private boolean isSpam(String line) {
+        return line.startsWith("Spam:");
+    }
+
+    /**
+     * Tell spamd that the given MimeMessage is a spam.
+     * 
+     * @param message
+     *            The MimeMessage to tell
+     * @throws MessagingException
+     *             if an error occured during learning.
+     */
+    public boolean learnAsSpam(InputStream message, String user) throws 
MessagingException {
+        return metricFactory.withMetric(
+            "spamAssassin-spam-report",
+            Throwing.supplier(
+                () -> reportMessageAs(message, user, MessageClass.SPAM))
+                .sneakyThrow());
+    }
+
+    /**
+     * Tell spamd that the given MimeMessage is a ham.
+     *
+     * @param message
+     *            The MimeMessage to tell
+     * @throws MessagingException
+     *             if an error occured during learning.
+     */
+    public boolean learnAsHam(InputStream message, String user) throws 
MessagingException {
+        return metricFactory.withMetric(
+            "spamAssassin-ham-report",
+            Throwing.supplier(
+                () -> reportMessageAs(message, user, MessageClass.HAM))
+                .sneakyThrow());
+    }
+
+    private boolean reportMessageAs(InputStream message, String user, 
MessageClass messageClass) throws MessagingException {
+        try (Socket socket = new Socket(spamdHost, spamdPort);
+             OutputStream out = socket.getOutputStream();
+             BufferedOutputStream bufferedOutputStream = new 
BufferedOutputStream(out);
+             PrintWriter writer = new PrintWriter(bufferedOutputStream);
+             BufferedReader in = new BufferedReader(new 
InputStreamReader(socket.getInputStream()))) {
+
+            byte[] byteArray = IOUtils.toByteArray(message);
+            writer.write("TELL SPAMC/1.2");
+            writer.write(CRLF);
+            writer.write("Content-length: " + byteArray.length);
+            writer.write(CRLF);
+            writer.write("Message-class: " + messageClass.value);
+            writer.write(CRLF);
+            writer.write("Set: local, remote");
+            writer.write(CRLF);
+            writer.write("User: " + user);
+            writer.write(CRLF);
+            writer.write(CRLF);
+            writer.flush();
+
+            out.write(byteArray);
+            out.flush();
+            socket.shutdownOutput();
+
+            return in.lines()
+                .anyMatch(this::hasBeenSet);
+        } catch (UnknownHostException e) {
+            throw new MessagingException("Error communicating with spamd. 
Unknown host: " + spamdHost);
+        } catch (IOException e) {
+            throw new MessagingException("Error communicating with spamd on " 
+ spamdHost + ":" + spamdPort, e);
+        }
+    }
+
+    private boolean hasBeenSet(String line) {
+        return line.startsWith("DidSet: ");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinResult.java
----------------------------------------------------------------------
diff --git 
a/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinResult.java
 
b/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinResult.java
new file mode 100644
index 0000000..96f98ea
--- /dev/null
+++ 
b/third-party/spamassassin/src/main/java/org/apache/james/spamassassin/SpamAssassinResult.java
@@ -0,0 +1,109 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.spamassassin;
+
+import java.util.Map;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+public class SpamAssassinResult {
+    /** The mail attribute under which the status get stored */
+    public static final String STATUS_MAIL_ATTRIBUTE_NAME = 
"org.apache.james.spamassassin.status";
+
+    /** The mail attribute under which the flag get stored */
+    public static final String FLAG_MAIL_ATTRIBUTE_NAME = 
"org.apache.james.spamassassin.flag";
+
+    public static final String NO_RESULT = "?";
+
+    public static SpamAssassinResult empty() {
+        return asHam()
+                .hits(NO_RESULT)
+                .requiredHits(NO_RESULT)
+                .build();
+    }
+
+    public static Builder asSpam() {
+        return new Builder(true);
+    }
+
+    public static Builder asHam() {
+        return new Builder(false);
+    }
+
+    public static class Builder {
+        
+        private String hits;
+        private String requiredHits;
+        private final boolean isSpam;
+
+        private Builder(boolean isSpam) {
+            this.isSpam = isSpam;
+        }
+
+        public Builder hits(String hits) {
+            this.hits = hits;
+            return this;
+        }
+
+        public Builder requiredHits(String requiredHits) {
+            this.requiredHits = requiredHits;
+            return this;
+        }
+
+        public SpamAssassinResult build() {
+            Preconditions.checkNotNull(hits);
+            Preconditions.checkNotNull(requiredHits);
+
+            ImmutableMap.Builder<String, String> headersAsAttribute = 
ImmutableMap.builder();
+            if (isSpam) {
+                headersAsAttribute.put(FLAG_MAIL_ATTRIBUTE_NAME, "YES");
+                headersAsAttribute.put(STATUS_MAIL_ATTRIBUTE_NAME, "Yes, 
hits=" + hits + " required=" + requiredHits);
+            } else {
+                headersAsAttribute.put(FLAG_MAIL_ATTRIBUTE_NAME, "NO");
+                headersAsAttribute.put(STATUS_MAIL_ATTRIBUTE_NAME, "No, hits=" 
+ hits + " required=" + requiredHits);
+            }
+
+            return new SpamAssassinResult(hits, requiredHits, 
headersAsAttribute.build());
+        }
+    }
+
+    private final String hits;
+    private final String requiredHits;
+    private final Map<String, String> headersAsAttribute;
+
+    private SpamAssassinResult(String hits, String requiredHits, Map<String, 
String> headersAsAttribute) {
+        this.hits = hits;
+        this.requiredHits = requiredHits;
+        this.headersAsAttribute = headersAsAttribute;
+    }
+
+    public String getHits() {
+        return hits;
+    }
+
+    public String getRequiredHits() {
+        return requiredHits;
+    }
+
+    public Map<String, String> getHeadersAsAttribute() {
+        return headersAsAttribute;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinExtension.java
----------------------------------------------------------------------
diff --git 
a/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinExtension.java
 
b/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinExtension.java
new file mode 100644
index 0000000..fd4980c
--- /dev/null
+++ 
b/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinExtension.java
@@ -0,0 +1,148 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.spamassassin;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Locale;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+
+import com.github.fge.lambdas.Throwing;
+
+public class SpamAssassinExtension implements BeforeEachCallback, 
AfterEachCallback, ParameterResolver {
+
+    private final GenericContainer<?> spamAssassinContainer;
+    private SpamAssassin spamAssassin;
+
+    public SpamAssassinExtension() {
+        spamAssassinContainer = new GenericContainer<>(
+            new ImageFromDockerfile()
+                .withFileFromClasspath("Dockerfile", 
"docker/spamassassin/Dockerfile")
+                .withFileFromClasspath("local.cf", 
"docker/spamassassin/local.cf")
+                .withFileFromClasspath("run.sh", "docker/spamassassin/run.sh")
+                .withFileFromClasspath("spamd.sh", 
"docker/spamassassin/spamd.sh")
+                .withFileFromClasspath("rule-update.sh", 
"docker/spamassassin/rule-update.sh")
+                .withFileFromClasspath("bayes_pg.sql", 
"docker/spamassassin/bayes_pg.sql"));
+        spamAssassinContainer.waitingFor(new 
SpamAssassinWaitStrategy(spamAssassinContainer));
+    }
+
+    @Override
+    public void beforeEach(ExtensionContext context) {
+        spamAssassinContainer.start();
+        spamAssassin = new SpamAssassin(spamAssassinContainer);
+    }
+
+    @Override
+    public void afterEach(ExtensionContext context) {
+        spamAssassinContainer.close();
+    }
+
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, 
ExtensionContext extensionContext) throws ParameterResolutionException {
+        return (parameterContext.getParameter().getType() == 
SpamAssassin.class);
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, 
ExtensionContext extensionContext) throws ParameterResolutionException {
+        return spamAssassin;
+    }
+
+    public SpamAssassin getSpamAssassin() {
+        return spamAssassin;
+    }
+
+    public static class SpamAssassin {
+        
+        private static final int SPAMASSASSIN_PORT = 783;
+
+        private final String ip;
+        private final int bindingPort;
+        private final GenericContainer<?> spamAssassinContainer;
+
+        private SpamAssassin(GenericContainer<?> spamAssassinContainer) {
+            this.spamAssassinContainer = spamAssassinContainer;
+            this.ip = spamAssassinContainer.getContainerIpAddress();
+            this.bindingPort = 
spamAssassinContainer.getMappedPort(SPAMASSASSIN_PORT);
+        }
+
+        public String getIp() {
+            return ip;
+        }
+    
+        public int getBindingPort() {
+            return bindingPort;
+        }
+
+        public void train(String user) throws IOException, URISyntaxException {
+            train(user, 
Paths.get(ClassLoader.getSystemResource("spamassassin_db/spam").toURI()), 
TrainingKind.SPAM);
+            train(user, 
Paths.get(ClassLoader.getSystemResource("spamassassin_db/ham").toURI()), 
TrainingKind.HAM);
+        }
+
+        private void train(String user, Path folder, TrainingKind 
trainingKind) throws IOException {
+            
spamAssassinContainer.getDockerClient().copyArchiveToContainerCmd(spamAssassinContainer.getContainerId())
+                .withHostResource(folder.toAbsolutePath().toString())
+                .withRemotePath("/root")
+                .exec();
+            try (Stream<Path> paths = Files.walk(folder)) {
+                paths
+                    .filter(Files::isRegularFile)
+                    .map(Path::toFile)
+                    .forEach(Throwing.consumer(file -> 
spamAssassinContainer.execInContainer("sa-learn",
+                        trainingKind.saLearnExtensionName(), "-u", user,
+                        "/root/" + trainingKind.name().toLowerCase(Locale.US) 
+ "/" +  file.getName())));
+            }
+        }
+
+        private enum TrainingKind {
+            SPAM("--spam"), HAM("--ham");
+
+            private String saLearnExtensionName;
+
+            TrainingKind(String saLearnExtensionName) {
+                this.saLearnExtensionName = saLearnExtensionName;
+            }
+
+            public String saLearnExtensionName() {
+                return saLearnExtensionName;
+            }
+        }
+
+        public void sync(String user) throws UnsupportedOperationException, 
IOException, InterruptedException {
+            spamAssassinContainer.execInContainer("sa-learn", "--sync", "-u", 
user);
+        }
+
+        public void dump(String user) throws UnsupportedOperationException, 
IOException, InterruptedException {
+            spamAssassinContainer.execInContainer("sa-learn", "--dump", 
"magic", "-u", user);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinInvokerTest.java
----------------------------------------------------------------------
diff --git 
a/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinInvokerTest.java
 
b/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinInvokerTest.java
new file mode 100644
index 0000000..473c6bf
--- /dev/null
+++ 
b/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinInvokerTest.java
@@ -0,0 +1,147 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.spamassassin;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.metrics.api.NoopMetricFactory;
+import org.apache.james.spamassassin.SpamAssassinExtension.SpamAssassin;
+import org.apache.james.util.MimeMessageUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(SpamAssassinExtension.class)
+public class SpamAssassinInvokerTest {
+
+    public static final String USER = "any@james";
+    private SpamAssassin spamAssassin;
+    private SpamAssassinInvoker testee;
+
+    @BeforeEach
+    public void setup(SpamAssassin spamAssassin) throws Exception {
+        this.spamAssassin = spamAssassin;
+        testee = new SpamAssassinInvoker(new NoopMetricFactory(), 
spamAssassin.getIp(), spamAssassin.getBindingPort());
+    }
+
+    @Test
+    public void scanMailShouldModifyHitsField() throws Exception {
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+                ClassLoader.getSystemResourceAsStream("eml/spam.eml"));
+        SpamAssassinResult result = testee.scanMail(mimeMessage, USER);
+
+        
assertThat(result.getHits()).isNotEqualTo(SpamAssassinResult.NO_RESULT);
+    }
+
+    @Test
+    public void scanMailShouldModifyRequiredHitsField() throws Exception {
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+                ClassLoader.getSystemResourceAsStream("eml/spam.eml"));
+        SpamAssassinResult result = testee.scanMail(mimeMessage, USER);
+
+        assertThat(result.getRequiredHits()).isEqualTo("5.0");
+    }
+
+    @Test
+    public void scanMailShouldModifyHeadersField() throws Exception {
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+                ClassLoader.getSystemResourceAsStream("eml/spam.eml"));
+        SpamAssassinResult result = testee.scanMail(mimeMessage, USER);
+
+        assertThat(result.getHeadersAsAttribute()).isNotEmpty();
+    }
+
+    @Test
+    public void scanMailShouldMarkAsSpamWhenKnownAsSpam() throws Exception {
+        spamAssassin.train("user");
+        
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+                
ClassLoader.getSystemResourceAsStream("spamassassin_db/spam/spam1"));
+
+        SpamAssassinResult result = testee.scanMail(mimeMessage, USER);
+
+        
assertThat(result.getHeadersAsAttribute().get(SpamAssassinResult.FLAG_MAIL_ATTRIBUTE_NAME)).isEqualTo("YES");
+    }
+
+    @Test
+    public void learnAsSpamShouldReturnTrueWhenLearningWorks() throws 
Exception {
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+                
ClassLoader.getSystemResourceAsStream("spamassassin_db/spam/spam2"));
+
+        boolean result = testee.learnAsSpam(mimeMessage.getInputStream(), 
USER);
+
+        assertThat(result).isTrue();
+    }
+
+    @Test
+    public void scanMailShouldMarkAsSpamWhenMessageAlreadyLearnedAsSpam() 
throws Exception {
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+                
ClassLoader.getSystemResourceAsStream("spamassassin_db/spam/spam1"));
+
+        byte[] messageAsBytes = 
MimeMessageUtil.asString(mimeMessage).getBytes(StandardCharsets.UTF_8);
+
+        testee.learnAsSpam(new ByteArrayInputStream(messageAsBytes), USER);
+
+        SpamAssassinResult result = testee.scanMail(mimeMessage, USER);
+
+        
assertThat(result.getHeadersAsAttribute().get(SpamAssassinResult.FLAG_MAIL_ATTRIBUTE_NAME)).isEqualTo("YES");
+    }
+
+    @Test
+    public void learnAsHamShouldReturnTrueWhenLearningWorks() throws Exception 
{
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+            ClassLoader.getSystemResourceAsStream("spamassassin_db/ham/ham2"));
+
+        boolean result = testee.learnAsHam(mimeMessage.getInputStream(), USER);
+
+        assertThat(result).isTrue();
+    }
+
+    @Test
+    public void scanMailShouldMarkAsHamWhenMessageAlreadyLearnedAsHam() throws 
Exception {
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+            ClassLoader.getSystemResourceAsStream("spamassassin_db/ham/ham1"));
+
+        testee.learnAsHam(mimeMessage.getInputStream(), USER);
+
+        SpamAssassinResult result = testee.scanMail(mimeMessage, USER);
+
+        
assertThat(result.getHeadersAsAttribute().get(SpamAssassinResult.FLAG_MAIL_ATTRIBUTE_NAME)).isEqualTo("NO");
+    }
+
+    @Test
+    public void learnAsHamShouldAllowToForgetSpam() throws Exception {
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+            ClassLoader.getSystemResourceAsStream("eml/spam.eml"));
+
+        byte[] messageAsBytes = 
MimeMessageUtil.asString(mimeMessage).getBytes(StandardCharsets.UTF_8);
+
+        testee.learnAsSpam(new ByteArrayInputStream(messageAsBytes), USER);
+        testee.learnAsHam(new ByteArrayInputStream(messageAsBytes), USER);
+
+        SpamAssassinResult result = testee.scanMail(mimeMessage, USER);
+
+        
assertThat(result.getHeadersAsAttribute().get(SpamAssassinResult.FLAG_MAIL_ATTRIBUTE_NAME)).isEqualTo("NO");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinResultTest.java
----------------------------------------------------------------------
diff --git 
a/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinResultTest.java
 
b/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinResultTest.java
new file mode 100644
index 0000000..fb75604
--- /dev/null
+++ 
b/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinResultTest.java
@@ -0,0 +1,96 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.spamassassin;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.assertj.core.api.JUnitSoftAssertions;
+import org.junit.Rule;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class SpamAssassinResultTest {
+
+    @Rule
+    public JUnitSoftAssertions softly = new JUnitSoftAssertions();
+
+    @Test
+    public void buildShouldThrowWhenHitsIsNotGiven() {
+        assertThatThrownBy(() -> SpamAssassinResult.asSpam()
+                .requiredHits("4.0")
+                .build())
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void buildShouldThrowWhenRequiredHitsIsNotGiven() {
+        assertThatThrownBy(() -> SpamAssassinResult.asSpam()
+                .hits("4.0")
+                .build())
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void buildShouldWork() {
+        String hits = "1.1";
+        String requiredHits = "5.0";
+
+        SpamAssassinResult spamAssassinResult = SpamAssassinResult.asSpam()
+            .hits(hits)
+            .requiredHits(requiredHits)
+            .build();
+
+        softly.assertThat(spamAssassinResult.getHits()).isEqualTo(hits);
+        
softly.assertThat(spamAssassinResult.getRequiredHits()).isEqualTo(requiredHits);
+        softly.assertThat(spamAssassinResult.getHeadersAsAttribute())
+            .containsAllEntriesOf(ImmutableMap.of(
+                SpamAssassinResult.FLAG_MAIL_ATTRIBUTE_NAME, "YES",
+                SpamAssassinResult.STATUS_MAIL_ATTRIBUTE_NAME, "Yes, hits=1.1 
required=5.0"));
+    }
+
+    @Test
+    public void 
headersAsAttributeShouldContainSpamHeaderWithYESValueWhenBuiltAsSpam() {
+        String hits = "1.1";
+        String requiredHits = "5.0";
+
+        SpamAssassinResult spamAssassinResult = SpamAssassinResult.asSpam()
+            .hits(hits)
+            .requiredHits(requiredHits)
+            .build();
+
+        assertThat(spamAssassinResult.getHeadersAsAttribute())
+            .containsEntry(SpamAssassinResult.FLAG_MAIL_ATTRIBUTE_NAME, "YES");
+    }
+
+    @Test
+    public void 
headersAsAttributeShouldContainSpamHeaderWithNOValueWhenBuiltAsHam() {
+        String hits = "1.1";
+        String requiredHits = "5.0";
+
+        SpamAssassinResult spamAssassinResult = SpamAssassinResult.asHam()
+            .hits(hits)
+            .requiredHits(requiredHits)
+            .build();
+
+        assertThat(spamAssassinResult.getHeadersAsAttribute())
+            .containsEntry(SpamAssassinResult.FLAG_MAIL_ATTRIBUTE_NAME, "NO");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1fd63a7f/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinWaitStrategy.java
----------------------------------------------------------------------
diff --git 
a/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinWaitStrategy.java
 
b/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinWaitStrategy.java
new file mode 100644
index 0000000..c9f1c7a
--- /dev/null
+++ 
b/third-party/spamassassin/src/test/java/org/apache/james/spamassassin/SpamAssassinWaitStrategy.java
@@ -0,0 +1,67 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.spamassassin;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
+import org.rnorth.ducttape.unreliables.Unreliables;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.WaitStrategy;
+import org.testcontainers.containers.wait.strategy.WaitStrategyTarget;
+
+import com.google.common.primitives.Ints;
+
+public class SpamAssassinWaitStrategy implements WaitStrategy {
+
+    private static final Duration DEFAULT_TIMEOUT = Duration.ofMinutes(1);
+    private final GenericContainer<?> spamAssassinContainer;
+    private Duration timeout = DEFAULT_TIMEOUT;
+
+    public SpamAssassinWaitStrategy(GenericContainer<?> spamAssassinContainer) 
{
+        this(spamAssassinContainer, DEFAULT_TIMEOUT);
+    }
+
+    public SpamAssassinWaitStrategy(GenericContainer<?> spamAssassinContainer, 
Duration timeout) {
+        this.spamAssassinContainer = spamAssassinContainer;
+        this.timeout = timeout;
+    }
+
+    @Override
+    public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) {
+        Unreliables.retryUntilTrue(Ints.checkedCast(timeout.getSeconds()), 
TimeUnit.SECONDS, () -> {
+                try {
+                    return spamAssassinContainer
+                        .execInContainer("spamassassin", "-V")
+                        .getStdout()
+                        .contains("SpamAssassin version 3.4.1");
+                } catch (IOException | InterruptedException e) {
+                    return false;
+                }
+            }
+        );
+    }
+
+    @Override
+    public WaitStrategy withStartupTimeout(Duration startupTimeout) {
+        return new SpamAssassinWaitStrategy(spamAssassinContainer, 
startupTimeout);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org
For additional commands, e-mail: server-dev-h...@james.apache.org

Reply via email to