Skip to content

Getting Started

matteo casadei edited this page Jun 23, 2017 · 43 revisions

Integrating Docet into an Existing Application

Integrating Docet into an existing application is a process involving two layers of your application's architecture: the server side and the client side.

First things first, integration occurs on the client side, in other words, in the layer running on the Web browser. This activity simply comes down to include a small javascript library and a base css into a Web page: the library takes care of shaping and issuing requests for pages, parsing responses and showing the corresponding content along with table of contents and navigation bars. The provided css allows for a base theme and layout which, in any way, can be customized accordinlgly to the look-and-feel of your application.

On the server side is it necessary to include the actual Docet engine so than requests for documentation pages coming to your backend can be forwarded to the engine which - via a locator service - will find the requested page, pre-process it (so as to correctly rendering links and others interactive content) and return the page ready to the client ready to be shown on the Web browser.

In the following sections, a step-by-step guide on how integrating Docet into your existing Java-based applications is provided: the code snipped adopted are taken from the sample application example included in the code under the docet-sample-app folder.

Requirements

In order to work Docet simply requires jQuery 1.x and Java 8.

On the Browser Side

Docet has been conceived as a single-page application: the simplest way to include it in your application is to define a Web page including the necessary javascript and css files, such as in the following sample code snippet:

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <script src="js/jquery.min.js" type="text/javascript"></script>
        <link href="docetres/docet/docet.css" type="text/css" rel="stylesheet" />
        <script src="docetres/docet/docet.js" type="text/javascript"></script>
    </head>
    <body>
        ...
    </body>
</html>

First of all, as shown above, docet.js javascript library and docet.css stylesheet are imported along with jQuery script, which is needed as docet.js relies on jQuery functions. The docet.css stylesheet provides the base classes that define a default look-and-feel and layout for the documentation: these classes can be overridden in order to devise a completely customized layout depending on the theme of the hosting application.

Second, it necessary to define a base structure of html elements that will be adpoted by the javascript engine to render the actual documentation. More specifically, Docet needs:

  • an html element acting as a "container" for the whole Docet Web application;
  • an html element acting as the anchor for the actual page content to be shown;
  • an html element necessary to host the table of contents (TOC) of the documentation;
  • one exploited in order to render the search bar;
  • one adopted to render a navigation bar (breadcrumbs);
  • an element acting as a footer, so that the js script can hook any content meant to be shown at the bottom of the page; Once defined in the html page, these elements can be arranged in a way that suits your need and annotated with a set of default css classes providing a base layout definition.
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <script src="js/jquery.min.js" type="text/javascript"></script>
        <link href="docetres/docet/docet.css" type="text/css" rel="stylesheet" />
        <script src="docetres/docet/docet.js" type="text/javascript"></script>
    </head>
    <body>
        <div id="docet">
            <div class="docet-titlebar-container">
                <div class="docet-layout">
                    <div id="docet-maintitle" class="docet-maintitle">Docet</div>
                </div>
            </div>
            <div class="docet-header-container">
                <div class="docet-layout">
                    <div id="crumbs-anchor" class="docet-breadcrumbs-container docet-breadcrumbs"></div>
                    <div id="docet-search" class="docet-search-container"></div>
                </div>            
            </div>
            <div id="docet-main-container" class="docet-main-container">
                <div class="docet-layout">
                    <div id="menu-anchor" class="docet-menu-container"></div>
                    <div id="content-anchor" class="docet-content-container docet-content"></div>
                </div>
            </div>
            <div id="docet-footer-container" class="docet-footer-container">
                <div class="docet-layout">
                    <div id="footer-anchor" class="docet-footer"></div>
                </div>
            </div>            
        </div>
        ...
    </body>
</html>

Then, Docet javascript needs to be instructed of the ids of the elements above and, also, properly configured with a few additional information necessary so that the library can behave correctly.

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <script src="js/jquery.min.js" type="text/javascript"></script>
        <link href="docetres/docet/docet.css" type="text/css" rel="stylesheet" />
        <script src="docetres/docet/docet.js" type="text/javascript"></script>
    </head>
    <body>
        <div id="docet">
            <div class="docet-titlebar-container">
                <div class="docet-layout">
                    <div id="docet-maintitle" class="docet-maintitle">Docet</div>
                </div>
            </div>
            <div class="docet-header-container">
                <div class="docet-layout">
                    <div id="crumbs-anchor" class="docet-breadcrumbs-container docet-breadcrumbs"></div>
                    <div id="docet-search" class="docet-search-container"></div>
                </div>            
            </div>
            <div id="docet-main-container" class="docet-main-container">
                <div class="docet-layout">
                    <div id="menu-anchor" class="docet-menu-container"></div>
                    <div id="content-anchor" class="docet-content-container docet-content"></div>
                </div>
            </div>
            <div id="docet-footer-container" class="docet-footer-container">
                <div class="docet-layout">
                    <div id="footer-anchor" class="docet-footer"></div>
                </div>
            </div>            
        </div>
        <script>
            Docet.init({
                elements: {
                    main: "#docet-main-container",
                    content: '#content-anchor',
                    menu: '#menu-anchor',
                    search: '#docet-search',
                    breadcrumbs: '#crumbs-anchor',
                    footer: '#footer-anchor',
                    footerContainer: '#docet-footer-container'
                },
                localization: {
                    pageTitle: "Docet's Sample App",
                    mainPageDescription: "",
                    language: 'en',
                    product: {
                        name: 'ACMe Docet\'s Sample app',
                        version: '0.0.1'
                    }
                },
                packages: {
                    list: ['sampledoc']
                },
                callbacks: {
                    response_error: function (response) {
                        var msg = '';
                        msg += 'Status code: ' + response.status + "\n";
                        msg += response.responseText;
                        console.log(msg);
                    },
                    search_error: function (res) {
                        var msg = '';
                        msg += "Error on searching on package " + res.packageid + "\n";
                        msg += "Message: " + res.errorMessage;
                        console.log(msg);
                    },
                    packagelist_error: function (res) {
                        var msg = '';
                        msg += "Error on retrieving info for package " + res.packageid + "\n";
                        msg += "Message: " + res.errorMessage;
                        console.log(msg);
                    },
                    page_changed: function (info) {
                        console.log('page changed', info);
                    }
                }
            });
            Docet.jumpToHomepage();
        </script>
    </body>
</html>

Configuration is done by calling method init with an input param consisting of a Javascript object containing the necessary configuration details:

  • elements allows to initialize Docet with the html id of the elements previously defined with the goal of rendering the different parts of the online documentation;
  • packages to specify a list of documentation packages available for browsing (though static in this example, it is very likely that a real application generates this list in a dynamic way);
  • localization information regarding localization such as language to be used on documentation packages along with a few additional info such as pagetitle, product name, etc. Below a list of values that can be configured (if left unspecified a default value will be used):
Name Description
mainPageDescription Message to show on the documentation dashboard (see below)
emptyPackageList Message to display on the main documentation dashboard when no documentation package is available for browsing
language A two-letter code corresponding to the language of the application (typically generated dynamically)
searchButtonLabel Label to show on the search button on the search bar
searchInputPlaceholder A message to be shown in the search box
searchResultTitle title to be shown of the page showing the results of a text search
showMoreResults label for the "show more results" button on the search results' page
noResultsFound Message to show on the search results' page when the searched word/sentence did not match any document
someResultsFound Message to show when the searched keyword produced actual results
topLink Label to use for the "back-to-top" button
  • callbacks that defines functions to be called by docet.js when the following circumstances occur:
    • page_changed, a documentation page is loaded
    • search_error, an error occurs during a search request
    • packagelist_error, an error occurs while retrieving the list of available packages (their description to be shown on the dashboad)
    • response_error, an error happens during retrieval of a documentation resource in general.

Finally, in order to actually initialize and start (the client side of) Docet, Docet.jumpToHomepage() is called so as to initialize any necessary front-end component and show the main dashboard reporting the list of available packages. An example of Web page rendered after invoking jumpToHomepage is shown below (of course for a correct rendering it is mandatory to have correctly integrated Docet on the server side as well).

Sample App Main Dashboard

On the Server Side

On the server side integration comes down to expose an instance of the docet runtime manager (an instance of Java class DocetManager) so as HTTP requests for documentation resources can be forwarded to the engine: to this end the class provides a method

public void serveRequest(HttpServletRequest request, HttpServletResponse response) throws DocetException

accepting an instance of HttpServletRequest and one of HttpServletResponse used by the engine to send responses to the client (browser). Accordingly, integration consists of setting up a servlet in your application so as to redirect requests/responses to the docet runtime manager. As an example, you can refer to docet-sample-app, specifically to DocetSimpleServlet:

package docet.servlets;

...

public class DocetSimpleServlet extends HttpServlet {

    private static final Logger LOGGER = Logger.getLogger(DocetSimpleServlet.class.getName());

    /**
     * Handles the HTTP <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ServletPath:" + request.getServletPath() + " " + request.getContextPath() + " " + request.getRequestURI());
        try {
            processRequest(request, response);
        } catch (DocetException ex) {
            LOGGER.log(Level.SEVERE, "Error on serving request, go to error page", ex);
            throw new ServletException(ex);
        }
    }

    /**
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws DocetException in case errors occurs on handling doc requests
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws DocetException {
        DocetManager docetEngine = (DocetManager) request.getServletContext().getAttribute("docetEngine");
        System.out.println("ServletPath:" + request.getServletPath() + " " + request.getContextPath() + " " + request.getRequestURI());
        docetEngine.serveRequest(request, response, new DocetSampleDocumentAccessor());
    }
}

Anyway, first of all it is necessary to initialize and create an instance of DocetManager: this can be achieved for instance by adopting a ServletContextListener in order to instantiate a DocetManager and add it to the ServletContext. As an example, take a look at DocetConfigurator in docet-sample-app:

package docet.servlets;

...

public class DocetConfigurator implements ServletContextListener {

    private static final Logger LOGGER = Logger.getLogger(DocetConfigurator.class.getName());

    @Override
    public void contextInitialized(ServletContextEvent ctx) {
        LOGGER.log(Level.SEVERE, "Docet is starting");
        ServletContext application = ctx.getServletContext();
        Properties configuration = new Properties();
        Properties docPackages = new Properties();
        try {
            configuration.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("docet.conf"));
            docPackages.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("docet-packages.conf"));

            final DocetConfiguration docetConf = new DocetConfiguration(configuration);
            docPackages.entrySet().stream().forEach(docPackage -> {
                Path packagePath = Paths.get(docPackage.getValue().toString());
                if (!packagePath.isAbsolute()) {
                    packagePath = Paths.get(application.getRealPath("/")).resolve(packagePath);
                }
                docetConf.addPackage(docPackage.getKey().toString(), packagePath.toString());
            });
            final DocetManager manager = new DocetManager(docetConf, new DocetSamplePackageLocator(docetConf));
            manager.start();
            application.setAttribute("docetEngine", manager);
            LOGGER.log(Level.SEVERE, "Docet configured, config:" + docetConf);
        } catch (DocetException | IOException e) {
            LOGGER.log(Level.SEVERE, "Impossible to properly setting up Docet configuration for Manager. ", e);
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        LOGGER.log(Level.INFO, "DOCet is shutting DOWN");
        try {
            ((DocetManager) sce.getServletContext().getAttribute("docetEngine")).stop();
        } catch (InterruptedException e) {
            LOGGER.log(Level.SEVERE, "Error on shutting down Docet. ", e);
            Thread.currentThread().interrupt();
        }
    }

}

Actual configuration parameters are taken from file docet.conf in docet-sample-app:

docet.debugmode=true
docet.version=${pom.version}

#package and searchindex path are relative to package docs main folder
#usually docet.package.docs.dirpath and docet.package.searchindex.dirpath
#do not need to be specified (here is done only for dev mode)
docet.package.docs.dirpath=main/docs
docet.package.searchindex.dirpath=../target/docet/index

docet.previewmode=true
docet.language.default=en
Clone this wiki locally