[Image] EchoPoint
Helping you build truly dynamic and stateful web applications!
nothing

Using the HtmlTemplatePanel and JspTemplatePanel components.

By Brad Baker
Wednesday, March 10, 2004

Development kudos for the HtmlTemplatePanel belongs to the wingS project.  It is a Swing based, dynamic web application framework just like echo.

The JspTemplatePanel was done as a result of a contribution from Sam Taha, so kudos to him as well.

You can find out more about Wings at http://wings.mercatis.de

Plenty Of Feel

When they invented the graphical user interface, somewhere in the Xerox Palo Alto design labs, someone invented the term "Look and Feel".  Its meant to describe the attributes of an application that affect how its looks and the functions of the application that affect how it feels when you use it.

Echo components, with their dynamic events and complete state management means that Echo applications have plenty of Feel.  Click anywhere you like, the fields keep their values.  Open the link in a new browser window, you are right back where you where before.

Echo applications are built by putting components within components.  For example to create an input form you might put RadioButtons, CheckBoxes and Textfields into a Grid, which you put inside another Grid, then into a Panel, ContentPane, ContainerPane and finally a Window.

While this approach means that you know where each component will be rendered (after all you coded it), the draw back is lots and lots of Java code as well as some intermediary components (like Grids) that are only there to put the necessary components on a certain place on the screen.   

It can be difficult the create a great Look via Java code alone.  And its probably even harder to change and maintain it with the sort of speed that a development project might demand.

All Look and No Feel

Now contrast this with the way modern HTML works.  HTML is all about defining where things should appear on a browser client.  Tables, table cells, style sheets, spacer images et al are all used to create a certain Look.  Tools such as DreamWeaver or Microsoft FrontPage can be used to create the HTML markup all with the drag and click of a mouse.

A bigger development project may have graphic designers on it whose job it is to create the Look of an application.  At the risk of perpetuating a common stereotype, this might by done by someone with a iMac and a long ponytail.

But the problem with HTML is that it is inherently static.  All Look and no Feel.  No events back to the server, no state management and definitely no dynamic updating of content.

Things like Java Server Pages (JSP) were created to address this problem but I believe JSP fundamentally has the wrong approach, i.e. taking a HTML file and making it execute. 

I believe a better approach is to use a HTML file as a template within an application to help describe how something should be laid out.  The HTML file is not the application however and should contain no application code.

The HtmlTemplatePanel and JspTemplatePanel component is designed to use HTML to describe the Look of the application, while at the same time retaining all the normal Echo functionality that completes the Feel.

Using The HtmlTemplatePanel Component

The HtmlTemplatePanel component will read a HTML file or URL and use the HTML content contained within to layout Echo components. 

The HtmlTemplatePanel component uses special SGML tag handlers to look for marker tags that indicate where to put Echo components.  The rest of the HTML markup is sent as is to the client browser.

This allows a HTML designer to create detailed layout, complete with spacer pixels and fixed size TDs etc.., which shows how the screen will be rendered.  The designer can use their favorite WYSWYG tools which create the HTML, independently of the Echo Java developer.

(In many projects that I have come across, the developer and the HTML designer are one and the same person, but I digress).

The HTML designer can then put special marker tags where the dynamic Echo components are to be placed. 

The HTML template text might look something like the following :

<HTML>
<BODY>

Name : <COMPONENT NAME="PERSON"></COMPONENT><BR>
Address: <COMPONENT NAME="ADDRESS"></COMPONENT><BR>
Phone : <COMPONENT NAME="PHONE"></COMPONENT><BR>

</BODY>
</HTML>

The COMPONENT tags will be substituted with the components that has been previously added via the HtmlTemplatePanel.addComponent() method. The components are matched via the constraint name and the SGML tag attribute name.

HtmlTemplatePanel templateContainer = new HtmlTemplatePanel(new File("mytemplate.html"));

templateContainer.add(new TextField("Name"),    "PERSON");
templateContainer.add(new TextField("Address"), "ADDRESS");
templateContainer.add(new TextField("Phone"),   "PHONE");

Note : Components will NEVER be created if they have NOT been added to the HtmlTemplatePanel. The template will, by design, fail to render them, but will instead insert a HTML garish message or comment noting their absence.  

The case of the active tags is not significant.  You can happily use <COMPONENT> or <component> or even <ComPonent> if you so wish. 

You can also self terminate tags as in  

<component name="xxx" />. 

You may also use the SGML tag attribute ID="xxx" for component naming.  For example:

      Name : <COMPONENT ID="PERSON"/>

Name-to-constraint matching is, however, case sensitive.  It is done via the Object.equals() method.  So in the precending example the following layout manager add call will not match the component associated with PERSON :

templateContainer.add(new TextField("Name"),    "person");


The current list of SGML tags handled are as follows:

  • <COMPONENT name="" background="" foreground="" font="" disabled=""  >
  • <OBJECT name="" background="" foreground="" font="" disabled=""  >   
    • (same as COMPONENT tag.  Left in for backwards compatibility)
  •  <INPUT type=radio name="" value="" checked >
  • <INPUT type=checkbox name="" value="" checked >
  • <INPUT type=button name="" value="" checked >
  • <BUTTON name="" icon="" checked>
  • <INPUT type=text name="" value="" size="n" bordercolor="" bordersize="" borderstyle="default|none|solid">
  • <INPUT type=password name="" value="" size="n" bordercolor="" bordersize="" borderstyle="default|none|solid">
  • <TEXTAREA name="" value="" rows="n" cols="n" bordercolor="" bordersize="" borderstyle="default|none|solid">
  • <TEXT name="" text="" >
  • <SELECT name="" >
    • <OPTION value="" selected >

Radio buttons are handled in a special way, in that their constraint names are matched using the tag name and value attribute. So if you had a template :

<input type=radio name="radioB" value="1">
<input type=radio name="radioB" value="2">

It would be matched if you added components such as:

templateContainer.add(new RadionButton("Button Title X"), "radioB1");
templateContainer.add(new RadionButton("Button Title Y"), "radioB2");

ListBox and SelectFields also have special handling. If the OPTION tag is present within the SELECT tag, then the model of the ListBox or SelectField will be replaced with a default one and the OPTION values will be added to this new model.

For example :

   <SELECT name="list1" >
      <OPTION value="Volvo" >
      <OPTION value="Ford" >
      <OPTION value="Fiat" selected >
   </SELECT>

... 
templateContainer.add(new ListBox(), "list1");
...

would cause the model of the listbox to be replaced with one with the values Volvo, Ford and Fiat where Fiat is the selected value.

Whereas the code :

...
<SELECT name="list1" ></SELECT>
...
or
...
<COMPONENT name="list1" ></COMPONENT>
...

would not replace the model of the ListBox or SelectField.

If you dont not add a component to the layout manager with a specified name, then the layout manager will render a very garish piece of HTML to indicate that the template design is broken.  I would argue that it does not make sense to have a <component name="..." /> marker in the template and then not provide a component for it.  Also it is better to catch "spelling" mistakes as early as possible in development rather than later. 

You can control whether a loud garish HTML message is displayed or whether a quiet HTML comment is used via the HtmlTemplatePanel.setLoudMessagesUsed() method.  

Using The JspTemplatePanel Component

The JspTemplatePanel can be used in a very similair way to the HtmlTemplatePanel. Instead of parsing an HTML file, this component uses the JSP capabilities of the J2EE application server to "include" the results of a JSP page.

This allows you to put beans in the HTTP request and session scope attributes or use your favourite JSP taglibs inside an Echo application.  The JSP page will be compiled and cached by the J2EE application server and hence future calls will be very fast. 

You need to provide the "path" of the JSP file to be used.  One you have done that you can add components that can then be references in the JSP markup itself.

JspTemplatePanel templateContainer = new JspTemplatePanel("/path/mymarkup.jsp"));

templateContainer.add(new Label("Name"),    "jsplabel");
templateContainer.add(new Button("Address"), "jspbutton");

An EchoPoint JSP taglib library is used to access the components that you have placed into the component. 

Just like any other JSP taglib library, you need to make sure you reference the taglib descriptor file echopointtags.tld in the JSP to ensure that it works. For example :

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/echopointtags.tld"
   prefix="echo" %>

Also make sure you include the taglibs support code in your web application deployment descriptor file web.xml as follows :

 ...
<!-- JspComponent Tag Library Descriptor -->
 <taglib>
     <taglib-uri>/WEB-INF/echopointtags.tld</taglib-uri>
  <taglib-location>/WEB-INF/echopointtags.tld</taglib-location>
 </taglib>
....

Also remember to put the echopointtags.tld file in the WEB-INF directory of your web application.  This will allow the JSP taglib to be used within your JSP markup and hence components can then be embedded.

The JSP markup might look something like :

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/echopointtags.tld"
   prefix="echo" %>
...
...
     <tr>
         <td>
           <echo:component name="jsplabel"/>
         </td>
      </tr>
      <tr>
         <td>
           <echo:component name="jspbutton"/>
         </td>
      </tr>
      ...

In this example two components will be included, inside the JSP output.  The first is called jsplabel and the other is called jspbutton

You can set the identifier of the component like this :

<echo:component name="jspbutton" id="theId" />

This will make a call to Component.setIdentifier() with the new value as long as the components current identifier is null. 

You can even put JavaBeans into the request scope.  You have three options.  You can use a single bean by calling  the contructor with a bean and a bean name like :

Object myBean = new String("Simple Bean Value");
JspTemplatePanel templateContainer = new JspTemplatePanel("/path/mymarkup.jsp", myBean, "jsp_example_bean"));

or you can add multiple static beans via bean name like this :

templateContainer.addBean(bean1, "jsp_example_bean");
templateContainer.addBean(bean2, "jsp_example_bean2");

or you can have add objects that implement echopoint.jsp.JspBeanGenerator, which will generate beans on the fly, to the JspTemplatePanel..

JspBeanGenerator myBeanGenerator = new JspBeanGenerator() {
   public Object generateBean() {
      return "Auto generated value here";
   };

templateContainer.addJspBeanGenerator(myBeanGenerator,"jsp_example_bean");

Either way the beans will be placed into the JSP as a HTTP request scope attribute and can be retrieved in the normal way, as shown below :

      <tr>
         <td>
          <%= request.getAttribute("jsp_example_bean") %>
         </td>
      </tr>

JSP Caveats

One caveat with JspTemplatePanel is that the JSP must be a HTML fragment, which means no <HTML> or <BODY> tags.  Unlike the HtmlTemplatePanel code, these tags will not be  stripped out of the source JSP file.  The reason for this is two fold. 

One is that app server is responsible for "compiling" the JSP source file into Java code.  Hence once this is done, then JSP output cannot be changed. 

Secondly taglibs are free to output their own HTML tags and hence the code cannot be sure what is going to result from calling the JSP page.  So just make sure you only use HTML fragments, ie anything that will fit inside a <BODY> tag.

Setting Component Properties via Tag Attributes.

The attributes within the <component name=".." /> tag are then applied to each component, if there is a PropertyManager available that understands them.. This is a piece of code that can translate attrName="attrValue" into a Component property setter.

All active tags have the following attributes which can be used to control the appearance of the components :

  • ID="xxx" - sets the identifier of the Component.
  • BACKGROUND="xxx" - sets the background
  • FOREGROUND="xxx" - sets the foreground
  • FONT="xxx" - sets the font
  • DISABLED - disables the component
  • STYLE="attr1:val1;attr2:val2...attrN:valN" - sets multiple style attributes at once

For a full list of attributes handled, see the various PropertyManager class descriptions.    

There is however a better and more natural way to specify Component property values and that is throught the use CSS style sheets.  This will be talked about in the next section

Setting Component Properties via CSS Style Sheets

As of EchoPoint 0.7, the ability to set style attributes for a templated component has been tremendously improved.  The same syntax and code that is used to provided EchoPoint CSS support is now available in HtmlTemplatePanel and JspTemplatePanel

For more information on EchoPoint CSS support click here.  

You simply put a style="...." attribute on the component tag, and the attribute value will be compiled into a style sheet and applied to the component.  For example in a HtmlTemapletPanel template you would use :

<component name="comp1" style="background : #ff0000;  font : font(verdana,bold,9); foreground : yellow">


The above will set the background, font and foreground properties of the embedded component named as "comp1".

JspTemplatePanel works in exactly the same manner as HtmlTemplatePanel, for example :

<echo:component name="comp1" style="background : #ff0000; font(verdana,bold,9); foreground : yellow" />

In both cases the component properties WILL only be set ONCE.  This is done the first time the template is encountered.  The reason for this is because component properties may be programmatically changed as the program is running, during which the template itself will be displayed many times.  You do not want to have the programattic changes lost by having the template evaluate the tag attributes in the markup value each time it is displayed.

Please note thatJspTemplatePanel does not have individual Component property setting.  For example you CANNOT do the following :

<echo:component name="comp1" background="#ff0000" font="verdana,bold,9" foreground="yellow" />

This  is because JSP 1.2 does not allow dynamic taglib attributes.  You have to name all your possible attributes at design time.  And while a guess could have been made about what attributes to support, this is not generic for any given component class.

While dynamic attributes are available in JSP 2.0 this is not the target servlet lib environment for EchoPoint.  This is largely irrelevant since setting multiple properties is just as powerful via the style="attr1:attrVal1;attr2:attrVal2....attrN:attrValN" tag attribute.

Embedding a CSS Style Sheet.

You can now embed a style sheet into the template and have it apply to all the chidlren of the template.  This features allows the style sheet information to reside in the same place as the template HTML markup.  For example using HtmlTemplatPanel, you could have a template like this :

<HTML>
<BODY>

<STYLESHEET>
nextapp.echo.Component {
     background : #0000ff;
     foreground : #ff0000;
}
</STYLESHEET>

Name : <COMPONENT NAME="PERSON"></COMPONENT><BR>
Address: <COMPONENT NAME="ADDRESS"></COMPONENT><BR>
Phone : <COMPONENT NAME="PHONE" STYLE="background:yellow"></COMPONENT><BR>

</BODY>
</HTML>

This would result in the all components in the HtmlTemplatePanel having a blue background and a red foreground.  However the component named as "PHONE" would have a yellow background and red foreground, since it used a style="..." attribute to specify more style sheet information.

If you are using JspTemplatePanel, then the template might look like this :

        <%@ page language="java" %>
        <%@ taglib uri="/WEB-INF/echopointtags.tld"
   prefix="echo" %>
         ....
         ....


     <echo:stylesheet>
   nextapp.echo.Component {
      background : #0000ff;
      foreground : #ff0000;
   }

     <echo:/stylesheet>


     <tr>
         <td>
           <echo:component name="jsplabel"/>
         </td>
      </tr>
      <tr>
         <td>
           <echo:component name="jspbutton" style="background:yellow"/>
         </td>
      </tr>
      ...

Again this would result in the all components in the JspTemplatePanel having a blue background and a red foreground.  However the component named as "jspbutton" would have a yellow background and red foreground, since it used a style="..." attribute to specify more style sheet information.

If you place invalid CSS style sheet information in the <stylesheet> or <echo:stylesheet> tags, then you will be presented with the same garish looking error message to indicate that your template is not correct.

Text Substitution

With EchoPoint version 0.2.6, a new text substitution feature has been added.  This allows portions of the HTML markup to be replaced with variable text from some other source.

Now you might rightly ask why do you need such a feature when you could simply have Label components inside the HTML markup.  The answer is the "it depends".

Using text substitution will cost less "memory" than creating an instance of a Label component for each peice of variable text.  A String with today's date in it is a much more lightweight object than a Label component that has a title set to today's date.  It also results in less HTML being sent back to the client.

But the counter argument is that you lose the flexibility provided by Echo components such as appearance "styling" and layout facilities. 

Text subsitition is accomplished by placing <TEXT> tags in the markup with a name attribute.  So for example :

...
<text name="named.text.1"></text>
<text name="named.text.2" />
...

would result in variable strings being placed into the template markup that have been associated with the names "named.text.1" and "named.text.2".

An object that implements echopoint.template.TextSubstitution is used to source the variable text each time the page is displayed.  The key method is

public String getSubstitutionText(String name);

A simple HashMap based implementation has been supplied called SimpleTextSubstitution.  This is simply a mapping of String names to String values.  You use it as follows :

SimpleTextSubstitution textSubs = new  SimpleTextSubstitution();
textSubs.put("named.text.1","value1");
textSubs.put("named.text.2",new Date());

You can of course provide your own implementation of TextSubstitution that might, for example, query a database or look in a message files for strings.  You can then set this into the HtmplTemplatePanel via the setTextSubstitution() method.

Think of this as a "Skin" for Echo web applications

One way to thinking of the HtmlTemplatePanel or JspTemplatePanel is that it can be used to create a "skin" for the web application. 

Just like Winamp skins or Netscape 6 skins, the HTML template will reside outside of, and independent of, the dynamic components contained within.

You can change the look of the template without affecting the contents inside or the working of the application.  You can tweak images within it or replace static colors schemes used.

With a bit of prior planning and some clever user interface design, it would be possible to deliver a web application that allows its look to be changed by replacing template files without affecting the application.

Of course any new template would have to contain the right <component> or <echo:component/> tags to ensure that the application worked as before and that all components are still visible to the user.


HtmlTemplatePanel Parsing

HTML parsing is done via a two pass process. The first "compilation" pass runs through the template content and marks the positions of the handled active tags.   Any attribute values in the template will be transferred to the appropriate component at this time.

The second "execution" pass will then copy the static content up until a remembered tag position and will then invoke the tag handler to allow a component to generate its content. Then the rest of the static content is copied until another remembered active tag position is reached and so on.

All DataSource objects have their last modified time checked. If they are up to date then only the second pass parse will be performed, and hence rendering time is reduced.

The HtmlTemplatePanel component has a caching scheme to ensure that parsed templates can be displayed as fast as possible.

The content of Files and URLs are cached internally if they are smaller than HtmlTemplatePanel.getFileCacheSizeLimit() and hence will execute very fast the next time they are rendered.   If the last modified time of the File changes, then a full parse will be done again and the changes will be reflected. 

This allows real time changing of templated content.  Emergency fixes, small touch ups, help desk phone number changes  etc  can then be performed outside the application Java code.

The page parser actually works on objects derived from the interface DataSource.

You can create your own class that implements DataSource, and have content come from anywhere, such as a database or Content Management System (CMS).   The following DataSource derived classes are provided

  • FileDataSource
    • reads its content from a file, with no caching.
  • CachedFileDataSource
    • reads its content from a file or URL and caches the data in memory if possible.
  • ReaderDataSource
    • reads its content from a java.io.Reader.
  • StreamDataSource
    • reads its content from a java.io.InputStream.
  • StringDataSource
    • reads its content from a string.

JspTemplatePanel Parsing

The JspTemplatePanel parsing relies on the JSP features of the J2EE application server and the java.servlet.jsp.PageContext interface to "include" a JSP file into the Echo web application.  Once a file has been parsed and compiled by the app server, it will run extrememly fast as it will most likely be compiled into a Java class.

The EchoPoint taglib component is used to include other Echo components.  They will be rendered inline with the other JSP output.  All of the JSP outyput will then be rendered alongside any other Echo output.

While it might sound a little confusing, the end result is that you can now have JSPs included inside an Echo application, and you can have Echo components included within JSP markup.

Stripping out the HTML and BODY tags.

The HtmlTemplatePanel has a feature that will strip out any HTML tags prior to and including the <BODY> tag.  The reason for this is that an HTML document cannot be included directly into another HTML document.  The extra <HTML> and <BODY> tags will clash and produce dodgy output on most browsers.

This also applies to XML in a really bad way.  How can you have enveloped XML without the ability to include an unescaped XML document in another is a major design flaw in the XML specification but again I digress, badly.

So the HtmlTemplatePanel has the ability to strip out all template content until just after the <BODY> tag.  From then on will display all template content until it reaches the ending </BODY> or </HTML> tags.

HtmlTemplatePanel even has a cascading style sheet fix up feature so that any style sheets included before the <BODY> tag will be captured and re-rendered within the Echo framework.  Both <LINK type="stylesheet" href="xxx"> and <STYLE>.....</STYLE> tags are supported.

It will also fix up any <STYLESHEET>...</STYLESHEET> tags and apply the contained EchoPoint CSS information within to the children of the template.

If the template does not contain the <HTML> or <BODY> tags, i.e. it is an HTML fragment, then well and good; no special handling need be applied.

The HTML/BODY stripping is provided so that graphical tools can be used to create the template HTML without the need for a developer edit before use.  They can then be maintained again by the graphical tool with strange things happening, because the HTML and BODY tag is not present.

As mention above, the JspTemplatePanel does not have any tags stripped out.

How to misuse the HtmlTemplatePanel and JspTemplatePanel components

A design goal of Echo is to have web application code firmly in domain of the Java developer (and possibly away from web designers, depending on your viewpoint).

Nextapp describe Echo on their web site as follows

"Echo removes the developer from having to think in terms of "page-based" applications and enables him/her to develop applications using the conventional object-oriented and event-driven paradigm for user-interface development. Knowledge of HTML, HTTP, and JavaScript is not required".

But as you can see the HtmlTemplatePanel changes this.  In order to  use an Html Template you must have some, albeit minimal, knowledge of HTML. 

You must as a minimum put the <component name="xxx"></component> tags somewhere inside the HTML.  Layout and positioning of components within an HtmlTemplatePanel is completely controlled via HTML coding.

You can get yourself into trouble if you use HTML tags such as anchors and Javascript.  Echo applications are NOT page based.  <A HREF=""> linking DOES NOT WORK in the traditional sense.  If you use anchor tags or JavaScript you may lead to the Echo application not executing as desired.

An Echo application must be suspended or exited before the user can follow an anchor link.  One way to achive an achor link might be embed a ExitButton or JavaScriptButton into the template.

You should stick to pure layout tags such as TABLEs, IMGs, TDs, DIVs and SPANs .  They can safely be used purely for layout reasons without affecting where the user can go. 

If you use style sheets, try to ensure that each tag element has a class="" or id="" attribute.  That way your style sheets will not interfere with other Echo components.  Chances are they will not, because Echo uses a unique naming scheme for its CSS tags, however you should do it just to be safe.

And try not to think too heavily in terms of pages.  Echo applications are NOT a series of pages that need to be linked.  They are an EchoInstance with a Window and replaceable components.  Your templates, as a rule, should be the layout structure, the Look framework within which Echo components reside. 

With HtmlTemplatePanel you should aim to break out much of the Look code and concentrate on what the application does.

Changes To The Templating Mechanism

In EchoPoint v0.2.4 the HTML templating functionality was moved to a HtmlTemplateLayoutManager.  The reason for this is that the layout manager can be used on any container that is LayoutManageable, not just HtmlTemplatePanel.  However the original HtmlTemplatePanel component has been retained and most of its method call directly through to an underlying HtmlTemplateLayoutManager.

In EchoPoint v0.2.6 the JSP templating functionality was added in much the same vein.  A JspTemplateLayoutManager is provided along with a JspTemplatePanel that uses this as a layout manager.

You can add a HtmlTemplateLayoutManager or JspTemplateLayoutManager to any container component that implements LayoutManageable.   So for example you could have a ScrollableBox or DialogPanel have its content laid out using these two layout managers.

Also the ability to force the "compilation" of a template before it is displayed has been added to HtmlTemplateLayoutManager.  The compileDataSource() method can now be called to force the template and component compilation process to occur.  Any attributes in the template will be added to the appropriate components during compilation.

Once this method has returned, you are then able to "override" any component properties set in the template.  When the template is then rendered, the compilation step will not occur again, since it has already been done. 

Finally a new setPropertiesUsed(boolean) method has been added to allow you to control whether Component properties are set from the markup source.  When this flag is false, no Components will have properties set during compilation.  By default this flag is true.

Credit Where Credit Is Due

All of the design for the HtmlTemplatePane and 70% of the code have come from the wingS project.  An email discussion started by Domagoj Jugovic inspired me to find out more about this project and its templating functionality.  I liked what I saw.

wingS is licenced under the GNU LGPL just like Echo and EchoPoint.  As such portions of its code can be re-used within the Echo framework.  wingS also took part of its code from the now defunct Apache JServ project, specifically the low level SGML tag parsing code.

Its a testament to open source software how code from many projects, even defunct ones, can help towards other projects.

You can find out more about Wings at http://wings.mercatis.de and Apache JServ at http://java.apache.org

Sam Taha made the initial contribution of the JspTemplatePanel to the EchoPoint project.  This was turned into a fully fledged layout manager but most of the code came from this original contribution.  So kudos to Sam as well.

Conclusion

I believe that the HtmlTemplatePanel and JspTemplatePanel components are a very powerful addition to the Echo framework.  Its use will allow better looking, more easily maintained and very robust web applications.

Let me know what you think of the components via my e-mail address brad dot baker @ candle dot com 


Home

SourceForge Logo

The EchoPoint project is kindly hosted by SourceForge