|
Threaded Indy News Reader - Felix John COLIBRI.
|
- abstract : a NewsReader which presents the articles sorted by thread and
in a logical hierarchical way: who answers to whom. It uses the basic Indy
NewsReader demo as a starting point
- key words : NNTP reader - news reader - threaded newsreader
- software used : Windows XP, Delphi 6, Indy 8.025
- hardware used : Pentium 2.800Mhz, 512 M memory, 140 G hard disc
- scope : Delphi 1 to 2006, Turbo Delphi for Windows, Kylix
- level : Delphi developer
- plan :
1 - Reading News
Newsgroup traffic is the biggest TCP/IP volume. For Delphi, newsgroups are
both the best place to find answers to narrow problems, and a huge knowledge
base.
You will find plenty of newsreaders around, even some free, and with sources.
Having very specific needs, we built our own, which originated in a Compuserve
BBS (Bulletin Board: the ancestor of the newsgroups) project. Recently, we
had to read SSL (Secure Socket Layer) protected newsgroups, but we had
not implemented the SSL layer. Since Indy includes the SSL part, we decided to
trade our old Windows Sockets engine for and Indy based one.
We first looked at the Delphi demo project:
C:\Program Files\Borland\Delphi6\Demos\Indy\NewsReader
which is a very fine demo. We used it as the starting point of this project.
Why didn't we use the Indy demo "as is". There are a couple of reasons
- first articles are presented in a sequential manner. For daily overview of
messages, that's fine. But for in-depth analysis, a presentation by thread
(by message topic, or subject), the hierarchical presentation is obviously
better suited
- we have added additional functionalities on top of the basic newsreader
By the way, "thread" in this context means "topic", and has nothing to do with
multitasking "threads".
2 - The Threaded Indy NewsReader
2.1 - The Basic Indy NewsReader
Just to recap, here are the steps to read news with Indy
|
drop a tIdNNtp Client on your tForm
Set the Host (forums.borland.com) and Port (119) properties
|
|
to fetch all the newsgroup from an Host, use:
IdNNTP1.Connect;
IdNNTP1.GetNewsgroupList(my_tStrings);
|
where my_tStrings is any tStrings descendent (tListBox.Items,
tMemo1.Lines, my_tstringlist etc)
|
|
you will get an entry for each news group, like
borland.public.delphi.jobs 22692 22481 y
borland.public.delphi.multimedia 7236 7046 y
borland.public.delphi.non-technical 607311 600469 y
borland.public.delphi.oleautomation 63539 62670 y
borland.public.delphi.oodesign 26836 26153 y
borland.public.delphi.opentoolsapi 12827 12641 y
borland.public.delphi.reporting-charting 40787 40504 y
|
|
|
once you have selected one of the newsgroups
(borland_public_delphi_oodesign for instance), you can request all the
messages within some id range:
IdNNTP1.SelectGroup('borland.public.delphi.oodesign');
IdNNTP1.SendXOVER('26153-26836', my_message_tStrings);
|
and ItNNTP1 will send the XOVER command to the news server, with the
message id range. The headers will be placed in the my_header_tStrings
tStrings descendent
|
|
here is a part of the answer (truncated in string length and message
count):
26809 | Why has OODB failed? | "William Egge" <begge@eggcentric.com>
| Mon, 4 Dec 2006
26810 | Re: Why has OODB failed? | Michael Baytalsky <mike@contextsoft.com>
| Mon, 04 Dec 200
26811 | Re: Why has OODB failed? | Franz-Leo Chomse
<franz-leo.chomse@samac.de> | Mon, 04 Dec 200
26812 | Re: Why has OODB failed? | "Wayne Niddery [TeamB]"
<wniddery@chaffaci.on.ca> | Mon, 4 Dec
26813 | Re: SmallTalk | "Peter Morris [Droopy eyes software]"
<pete@NO_droopyeyes_SPAM.
26814 | Re: Why has OODB failed? | Andreas Dorn <adornno1@web.de>
| Mon, 04 Dec 200
26815 | Re: SmallTalk | "Jarle Stabell"
<jarle@remove_stuff_dlogikk_spam_kills_email.co
26816 | Re: Why has OODB failed? | "Wayne Niddery [TeamB]"
<wniddery@chaffaci.on.ca> Tue, 5 Dec
26817 | Re: Why has OODB failed? | "Bob Dawson" <RBDawson@prodigy.net>
| Tue, 5 Dec 2006
26818 | Onion | "Peter Morris [Droopy eyes software]"
<pete@NO_droopyeyes_SPAM.
26819 | TTreeView.ValidateSelection not virtual nor dynamic?! | Ricardo
Villela Coppola <coppola@c
26820 | Re: SmallTalk | "Peter Morris [Droopy eyes software]"
<pete@NO_droopyeyes_SPAM.
26821 | Re: SmallTalk | "Bart" <@>
| Fri, 8 Dec 2006
26822 | Re: SmallTalk | "Peter Morris [Droopy eyes software]"
<pete@NO_droopyeyes_SPAM.
26823 | Re: SmallTalk | "Bart" <@>
| Fri, 8 Dec 2006
26824 | Re: Why has OODB failed? | "John Herbster"
<herb-sci1_AT_sbcglobal.net> | Fri, 8 Dec 2006
26825 | Re: Why has OODB failed? | "Harley Pebley"
<harley_pebley@idahotech.com> | 8 Dec 2006 16:4
26826 | Re: Why has OODB failed? | Michael Baytalsky <mike@contextsoft.com>
| Sat, 09 Dec 200
26827 | Re: TTreeView.ValidateSelection not virtual nor dynamic?! | "Bob
Dawson" <RBDawson@prodigy
|
We have truncated the length of each line, and replaced the Tab (Chr(9))
with a "|"
A full header line will look like
26809
Why has OODB failed?
"William Egge"
<begge@eggcentric.com>
Mon, 4 Dec 2006 08:47:08 -0500
<457426d2$1@newsgroups.borland.com>
2156
39
Xref: newsgroups.borland.com borland.public.delphi.oodesign:26809
|
|
|
to donwload a single message (article), we drop a tIdMessage on your
tForm:
- request the message (Wayne Niddery's answer to William Egge in our case)
using
IdNNTP1.GetArticle(26812, '', IdMessage1);
my_sting:= TIdText(IdMessage1.MessageParts.Items[0]).Body.Text;
|
|
|
if we display the body of the message in a tMemo, we would see
(truncated):
William Egge wrote:
> I am curious why OODB has failed to take hold. It can"t be because of
> needing compaitibility with other systems because an OODB should be
> able to provide a relational interface for legacy applications.
The model simply doesn"t work at the persistence level - one does not need
to store *objects* (which include behaviour) one just needs to store the
data of those objects.
Storing entire objects as some kind of blob is counter-productive because it
cannot be effectively and efficiently queried, and it also inefficient since
not all of attributes of an object *should* be stored - one of the nice
things about objects is they can have derived, non-persistent attributes and
relations.
|
|
So the tIdMessage is simply a component that analyzes the message's content.
In our case, sending the basic Windows Socket ARTICLE request
my_nntp_socket.Send('ARTICLE 26812')
|
would have brought back the following text (truncated):
220 26812 <457449dc$1@newsgroups.borland.com> article retrieved - head and body follows
Reply-To: "Wayne Niddery [TeamB]" <wniddery@chaffaci.on.ca>
From: "Wayne Niddery [TeamB]" <wniddery@chaffaci.on.ca>
Newsgroups: borland.public.delphi.oodesign
References: <457426d2$1@newsgroups.borland.com>
Subject: Re: Why has OODB failed?
Date: Mon, 4 Dec 2006 11:17:08 -0500
Lines: 69
X-Priority: 3
X-MSMail-Priority: Normal
X-Newsreader: Microsoft Outlook Express 6.00.2900.2869
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2962
X-RFC2646: Format=Flowed; Response
NNTP-Posting-Host: 69.17.150.141
Message-ID: <457449dc$1@newsgroups.borland.com>
X-Trace: newsgroups.borland.com 1165248988 69.17.150.141 (4 Dec 2006 08:16:28 -0700)
X-Authenticated-User: wniddery
Path: newsgroups.borland.com!not-for-mail
Xref: newsgroups.borland.com borland.public.delphi.oodesign:26812
William Egge wrote:
> I am curious why OODB has failed to take hold. It can"t be because of
> needing compaitibility with other systems because an OODB should be
> able to provide a relational interface for legacy applications.
The model simply doesn"t work at the persistence level - one does not need
to store *objects* (which include behaviour) one just needs to store the
data of those objects.
Storing entire objects as some kind of blob is counter-productive because it
cannot be effectively and efficiently queried, and it also inefficient since
not all of attributes of an object *should* be stored - one of the nice
things about objects is they can have derived, non-persistent attributes and
relations.
|
Therefore, using the Indy components has the following benefits:
- we do not have to wait for and assemble the different TCP/IP packets
- the status code (220) is analyzed for us
- the intersting parts of the header (FROM, SUBJECT etc) are placed in
different properties of the component
- if the message is a multipart message, they are also separated in the
tIdMessage.MessageParts.Items array. As I understand, Borland newsgroup
Netiquette discourage using attachments (they are stored in a separate
newsgroup), but they could be handy for some other Server
2.2 - Threaded NewsReader
Requesting messages by id (26.812 in our case) does not present a logical view
of the conversation taking place:
- several topic messages are intermingled
- there is no indication to "who answers to whom"
For instance, using the original NTTP newsreader demo, for the "oodesign" group
and the first "OODb" message, we would get:
You will notice that the "OODb" messages are mixed with the other messages
("Smalltalk" in our case)
To somehow represent the thread, we have to
- sort the messages by thread
- in each thread, display the hierarchical nature of the discussion
Here is an example of the hierarchy of the "OODb" thread:
Why has OODB failed?
William Egge <begge@eggcentric.com> [26809]
Michael Baytalsky <mike@contextsoft.com> [26810]
John Herbster <herb-sci1_AT_sbcglobal.net> [26824]
Michael Baytalsky <mike@contextsoft.com> [26826]
Harley Pebley <harley_pebley@idahotech.com> [26825]
Franz-Leo Chomse <franz-leo.chomse@samac.de> [26811]
Wayne Niddery [TeamB] <wniddery@chaffaci.on.ca> [26812]
Andreas Dorn <adornno1@web.de> [26814]
Wayne Niddery [TeamB] <wniddery@chaffaci.on.ca> [26816]
Bob Dawson <RBDawson@prodigy.net> [26817]
|
This is the purpose of the present project.
In fact, we presented already how to build the message
tree structure in a detailed paper, with the algorithm we used, UML Class
diagram and the UNIT which builds the tree. We are simply going to
incorporate the CLASSes in the current project
2.3 - The Delphi Threaded NewsReader
We used the same basic organization as the Demo. Here are the modifications:
- we removed the SSL parts, not required in our case
- we simplified the newsgroup tListView, and added a simple optional "Delphi"
filter
- we removed the list of messages from a group
and we added the following parts
- we replaced the list of message by a thread list
- clicking on an Item of the thread list displays
- either a tTreeView of the messages, and clicking on on of those fetches
the content of the message
- or we fetch all the messages of the thread and present them indented
2.4 - Mini User Guide
The easiest way to understand what our project is to look at a reading session:
|
compile and run the p_threaded_news_reader project
|
|
the project is displayed
|
|
connect the reader to your server by clicking "connect_"
|
|
all available newsgroups are displayed, along with the currently available
message count:
Note that
- since we checked the delphi_ tCheckbox, our project only presents the
Delphi newsgroups. We have no need for any C++ or other Java stuff.
- we also removed the "borland.public.delphi." prefix which does not add
anything to the display
- and we removed the empty groups
|
|
to display the messages from a given newsgroup, click on the target
newsgroup
|
|
the project sends the "XOVER" NNTP command to fetch the message headers:
|
|
after 5 seconds (ADSL, 512K), they are here, and our U_C_NEWS_THREAD_LIST
UNIT orders them by thread, and presents them, along with their starting
date (month/year in mm-yy format):
|
|
to the message from a thread on by one, select the "article_" tab from the
right tNoteBook, and click on one of the thread in the middle tTreeview.
In our case, we chose "UML Class Diagram From Source"
|
|
the tTreeView displays all message headers in an indented fashion:
|
|
clicking on any message in the tTreeview requests the message content and
displays it in a tMemo:
|
|
to view all the message from a thread, select the "articleS_" tab from
the right tNoteBook, and click on one of the thread in the middle
tTreeview. In our case, we chose "UML Class Diagram From Source"
|
|
the tMemo displays all the messages in an indented fashion. Here are the
first message, and the start of Gerrit Beuze's answer:

|
Concerning the topic of the thread
- Mr Remo Candeli is somehow hard with Model Maker. I still use it for our UML
Trainings, when customers do not wish to use Together
- Gerrit Beuze from ModelMaker Tools answers very nicely and even provides
links to Model Maker UML analysis tools
- to the list provided by Gerrit Beuze, I would also add the excellent, free,
open source .PAS to UML Class diagram Ess-Model tool
All those comments have nothing to do with threaded newsreader, but each time I
read a topic, I have a hard time to stop reading. Newsgroups are such an
information gold mine...
2.5 - What's Next ?
We now have our basic message download tool, as well as a hierarchical article
display. The true work will start from there.
Reading messages every morning is fine, but you cannot build a knowledge base
unless you somehow save the text, and create either an indexing scheme, or a
search engine.
Whenever I look for some information, I simply go to Tamarack an try to find some keyword that
their Rubicon engine will use to locate relevant messages. The main reason I am
using Tamarack, like so many of you, is that Tamarack has saved all the Delphi
messages, and offers an Internet querying system to get the information out of
this database.
What I would prefer is to download the archive, of, say, the last 5 years, and
use our own Full Text Search engine which has
a better requesting system (in my opinion). You have the full Pascal logic at
your fingertips. To get tools other than ModelMaker which extract the UML Class
diagram from source code we could ask:
|
"UML Class diagram" AND (extract OR "reverse engineering"
OR "from source") AND NOT (ModelMaker OR "Model
Maker")
|
I am not sure that Rubicon can handle such requests.
So why havn't we already implemented this machinery ? Well, our Delphi
newsgroup archives are incomplete. When some customer projects dragged us into
heavy programming, donwloading the news was often neglected. Maybe we will
build an automatic tool which will do the downloading each morning, to avoid
those gaps in our archives.
But this brings me to another pet idea of mine: why did Borland never offered
the FULL archives as .ZIP files ? Are there some legal reasons (copyrights,
maybe ?). Or disk space ? Or lack of interest from the developers ?
I mentioned it when I met some CodeGear executives, early October 2006, but
have not received any answer so far. Maybe if many of you push for this .ZIP
archive, and if the previous stuff has not yet been scrapped, we could get this
fabulous knowledge base ? And if I can put my hands on the .ZIPs, I will
publish the adapted search engine.
3 - Download the Sources
Here are the source code files:
The .ZIP file(s) contain:
- the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any
other auxiliary form
- any .TXT for parameters, samples, test data
- all units (.PAS) for units
Those .ZIP
- are self-contained: you will not need any other product (unless expressly
mentioned).
- for Delphi 6 projects, can be used from any folder (the pathes are RELATIVE)
- will not modify your PC in any way beyond the path where you placed the .ZIP
(no registry changes, no path creation etc).
To use the .ZIP:
- create or select any folder of your choice
- unzip the downloaded file
- using Delphi, compile and execute
To remove the .ZIP simply delete the folder.
The Pascal code uses the Alsacian notation, which prefixes identifier by
program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre,
F_unction, C_lasse etc. This notation is presented in the Alsacian Notation
paper.
As usual:
- please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs or had
some problem downloading the file. Resulting corrections will be helpful
for other readers
- we welcome any comment, criticism, enhancement, other sources or reference
suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
- or more simply, enter your (anonymous or with your e-mail if you want an
answer) comments below and clic the "send" button
- and if you liked this article, talk about this site to your fellow
developpers, add a link to your links page ou mention our articles in
your newsgroup posts when relevant. That's the way we operate: the more
traffic and Google references we get, the more articles we will write.
4 - References
- the basic Indy NewsReader demo is in the Delphi Demo folder
- the construction of a thread tree is presented our the
message tree structure paper, with the algorithm we used, UML Class
diagram and the UNIT which builds the tree.
- Indy components are on Delphi's Palette, but you can find additional
information, a knowledgebase and additional demos at the Indy Project web site
5 - The author
Felix John COLIBRI works at the Pascal
Institute. He programs in Pascal since 1979, and is mainly active in the area
of custom software
development and training, and is a frequent speaker at Borland
Developer Conferences. His web site features
tutorials, technical papers about programming with full downloadable source
code, and the description and calendar of forthcoming Delphi,
Interbase, Asp.Net, Ado.Net and OOP / UML training sessions.
|