com.smash.revolance.ui.explorer.UserExplorer.java Source code

Java tutorial

Introduction

Here is the source code for com.smash.revolance.ui.explorer.UserExplorer.java

Source

package com.smash.revolance.ui.explorer;

/*
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Revolance-UI-Explorer
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Copyright (C) 2012 - 2013 RevoLance
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

import com.smash.revolance.ui.model.application.Application;
import com.smash.revolance.ui.model.element.api.Element;
import com.smash.revolance.ui.model.element.api.ElementBean;
import com.smash.revolance.ui.model.helper.*;
import com.smash.revolance.ui.model.page.api.Page;
import com.smash.revolance.ui.model.page.api.PageBean;
import com.smash.revolance.ui.model.sitemap.SiteMap;
import com.smash.revolance.ui.model.user.User;
import org.apache.log4j.*;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebDriver;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * User: wsmash
 * Date: 02/06/13
 * Time: 12:35
 */
public class UserExplorer extends Thread {
    private Logger log = Logger.getLogger(UserExplorer.class);

    private int timeoutInSec;
    private User user;
    private File reportFile;

    public UserExplorer(User user, WriterAppender log, File reportFile, int timeoutInSec) {
        this.user = user;
        this.log.addAppender(log);
        this.user.setLogger(this.log);
        this.reportFile = reportFile;
        this.timeoutInSec = timeoutInSec;
    }

    public UserExplorer(User user, File reportFolder, int timeoutInSec) throws IOException {
        this(user,
                new FileAppender(new SimpleLayout(), File
                        .createTempFile("exploration-" + UUID.randomUUID().toString(), ".log").getAbsolutePath()),
                reportFolder, timeoutInSec);
    }

    public File doContentReport() {
        try {
            JsonHelper.getInstance().map(getReportFile(), getSiteMap());
            getLogger().log(Level.INFO, "Report has been generated with id: " + getReportFile().getName());
        } catch (IOException e) {
            e.printStackTrace();
            getLogger().log(Level.ERROR, "Unable to generate report!");
        }
        return getReportFile();
    }

    public File getReportFile() {
        return reportFile;
    }

    public Logger getLogger() {
        return log;
    }

    public void run() {
        try {
            this._explore();
        } catch (Exception e) {
            getLogger().log(Level.ERROR, "Exploration aborted. Reason: " + e.getMessage());
            getLogger().log(Level.ERROR, e);
        } finally {
            user.setExplorationDone(true);
            try {
                user.stopBot();
                doContentReport();
            } catch (Exception e) {
                getLogger().log(Level.ERROR, e);
            }
        }
    }

    public void explore() {
        start();

        if (timeoutInSec == 0) {
            return; // We don't want to monitor the duration of the exploration
        } else {
            long mark = System.currentTimeMillis();
            while ((System.currentTimeMillis() - mark) < timeoutInSec * 1000) {
                try {
                    sleep(10000); //10s of sleep
                } catch (InterruptedException e) {

                }
                if (user.isExplorationDone()) {
                    return;
                }
            }

            user.setExplorationDone(true);
        }
    }

    private User _explore() throws Exception {
        long start = System.currentTimeMillis();
        Page home = new Page(user, user.getHome());

        user.goTo(home).awaitLoaded();
        new PageParser(home).parse(); // Just for the home page
        if (!home.hasBeenExplored()) {
            if (!home.isExternal() && !home.isBroken()) {
                // This call will recursively:
                // parse & handleUserAccout logic & explore
                // of all the pages accessible from home
                explore(home);
                home.setExplored(true);
            } else if (home.isBroken()) {
                home.getSource().setBroken(true);
            }
        }

        user.setExplorationDone(true);
        user.stopBot();

        long duration = System.currentTimeMillis() - start;
        user.getLogger().log(Level.INFO, "Exploration [Done] [Duration " + duration / 60000 + "mn]");

        return user;
    }

    private void _explore(Page page) throws Exception {
        doRecursiveExploration(page);
        doContentReport();
    }

    private void doRecursiveExploration(Page page) throws Exception {
        String title = page.getTitle();
        user.getLogger().log(Level.INFO, "Exploring page: " + title);
        if (page.isOriginal()) {
            exploreOriginal(page);
            user.getLogger().log(Level.INFO, "Exploring page: " + title + " [Done]");
        } else {
            exploreVariant(page);
            user.getLogger().log(Level.INFO, "Exploring variant [Done]");
        }
    }

    private void exploreVariant(Page page) throws Exception {
        if (!page.getContent().isEmpty()) {
            user.getLogger().log(Level.INFO,
                    "New content has been found in this variant. Exploration will begin...");
            _explore(page);
            user.getLogger().log(Level.INFO, "New content has been found in this variant. Exploration [Done]");
        } else {
            user.getLogger().log(Level.INFO,
                    "No variations have been found. Deleting variant and associated content...");
            page.delete(); // Remove itself
            user.getLogger().log(Level.INFO, "No variations have been found. Deletion [Done]");
        }
    }

    public void explore(Page page) throws Exception {
        new PageParser(page).parse();
        handleUserAccountLogic(page);

        if (!page.hasBeenExplored() && !page.isBroken() && !page.isExternal()) {
            List<Element> clickables = page.getClickableContent();
            Collections.sort(clickables);
            for (Element clickable : clickables) {
                if (!clickable.hasBeenClicked() && clickable.isClickable() && !clickable.isDisabled()
                        && BotHelper.rightDomain(user, clickable.getHref())) {
                    // Checking if some content is left to be explored on the target page (if already being explored)
                    if (!getSiteMap().hasBeenExplored(UrlHelper.removeHash(clickable.getHref()))
                            && !UrlHelper.areEquivalent(UrlHelper.removeHash(clickable.getHref()),
                                    UrlHelper.removeHash(page.getUrl()))
                            && !getSourceContent(page).contentEquals(clickable.getContent())) {

                        String oldWindowHandle = getBrowser().getWindowHandle();
                        int windowCount = getBrowser().getWindowHandles().size();

                        if (clickable.click()) {
                            if (getBrowser().getWindowHandles().size() > windowCount) {
                                exploreNewWindowChanges(page, clickable, oldWindowHandle);
                            } else {
                                exploreChanges(page, clickable);
                            }
                        }
                    } else {
                        // Either the target page has already been explored or we're already exploring the page
                        clickable.setClicked(true);
                    }

                }
            }
        }
    }

    private String getSourceContent(Page page) {
        Element source = page.getSource();
        if (source == null) {
            return "";
        } else {
            return source.getContent();
        }
    }

    private boolean isUnexploredContentLeadingToThisPage(Page page, Element newClickElement) throws Exception {
        ElementBean target = getSiteMap().getFirstUnexploredElement(newClickElement.getUrl());
        if (target == null) {
            return false;
        } else {
            String currentUrl = UrlHelper.removeHash(page.getUrl());
            String targetUrl = UrlHelper.removeHash(target.getPage().getUrl());
            return UrlHelper.areEquivalent(currentUrl, targetUrl);
        }
    }

    private void exploreNewWindowChanges(Page prevPage, Element clickable, String oldWindowHandle)
            throws Exception {
        WebDriver oldBot = swithToNewWindow(oldWindowHandle);

        exploreChanges(prevPage, clickable);

        // Closing the new window after exploration has completed
        WebDriver oldOldBot = user.setBrowser(oldBot);
        oldOldBot.close();

        getBrowser().switchTo().window(oldWindowHandle);
    }

    private WebDriver swithToNewWindow(String oldWindowHandle) throws Exception {
        WebDriver oldBot;
        Set<String> handles = user.getBrowser().getWindowHandles();
        String newHandle = "";
        for (String handle : handles) {
            if (!handle.contentEquals(oldWindowHandle)) {
                newHandle = handle;
                break;
            }
        }

        oldBot = user.setBrowser(getBrowser().switchTo().window(newHandle));
        UserHelper.handleAlert(user);

        // Adapting the size of the new window
        Dimension size = new Dimension(user.getBrowserWidth(), user.getBrowserHeight());
        getBrowser().manage().window().setSize(size);

        return oldBot;
    }

    private void exploreChanges(Page page, Element clickable) throws Exception {
        String currentUrl = getBrowser().getCurrentUrl();
        if (!UrlHelper.areEquivalent(currentUrl, page.getUrl())) {
            handleUrlChange(page, clickable, currentUrl);
        }
        /*
        else // url are equivalent (without hash) so we've to check if this is a dynamic change in the content
        {
        if ( user.isPageScreenshotEnabled() )
        {
            handleDynamicChange( page, clickable );
        }
        else
        {
            clickable.setDisabled( true ); // nothing seems to have changed
        }
        }
        */
    }

    private void handleDynamicChange(Page page, Element source) throws Exception {
        String id = UUID.randomUUID().toString();
        String caption = BotHelper.pageContentHasChanged(user.getBot(), page.getCaption());
        if (!caption.contentEquals(page.getCaption())) {
            // Create new page and inject data to speed up the computations

            // Checking if there is already a variant with that checksum
            Page variant = page.findVariantByCaption(caption);

            // otherwise we've to create one from scratch
            if (variant == null) {
                variant = page.findVariantBySource(source.getBean());
            }

            if (variant == null) {
                variant = _buildVariant(page, source, false);
                variant.setId(id);

                variant.setImage(ImageHelper.decodeToImage(caption));

                page.addVariant(variant);

                // Parse the new content.
                new PageParser(variant).parse();
                removeBaseContent(variant);

                if (user.wantsToExploreVariants()) {
                    explore(variant);
                }
            }

            source.setDisabled(false);
        }
    }

    private void removeBaseContent(Page page) throws Exception {
        List<Element> content = page.getContent();
        List<Element> contentToRemove = new ArrayList<Element>();

        for (Element element : content) {
            if (page.getOriginal().getBean().contains(element.getBean())) {
                contentToRemove.add(element);
            }
        }

        content.removeAll(contentToRemove);
        page.setContent(content);
    }

    private void handleUrlChange(Page prevPage, Element clickable, String currentUrl) throws Exception {
        if (UrlHelper.containsHash(currentUrl)) {
            String hash = UrlHelper.getHash(currentUrl);
            Page variant = getVariant(prevPage, clickable, hash, true).getInstance();

            variant.setScrollX(String.valueOf((Long) user.getBot().runJS("return window.scrollX")));
            variant.setScrollY(String.valueOf((Long) user.getBot().runJS("return window.scrollY")));

            prevPage.addVariant(variant);
        } else if (BotHelper.rightDomain(prevPage.getUser(), currentUrl))// Real change of page
        {
            Page page = getSiteMap().getPage(currentUrl, true).getInstance();
            if (!page.hasBeenExplored() && !page.isExternal()) {
                page.setSource(clickable);
                explore(page);
            }
        }
    }

    private void exploreOriginal(Page page) throws Exception {
        if (!page.isBroken()) {
            user.getLogger().log(Level.TRACE, "Exploring the original version of the page: " + page.getTitle());

            explore(page);
        }
    }

    private void handleUserAccountLogic(Page page) throws Exception {
        Application application = getApplication();
        if (application != null && application.isLoginPage(page)) {
            getApplication().login(user, page);
        }
    }

    /**
     * Retrieve a variant given the source element of the page. Do not generate it if not found.
     *
     * @param source
     * @return
     * @throws Exception
     */
    public Page getVariant(Page prevPage, ElementBean source) throws Exception {
        return getVariant(prevPage, source, false);
    }

    /**
     * Retrieve a variant given the source element of the page
     * Build the variant if not existing.
     *
     * @param source
     * @return
     * @throws Exception
     */
    public Page getVariant(Page prevPage, ElementBean source, boolean generate) throws Exception {
        Page page = prevPage.findVariantBySource(source);
        if (page != null) {
            return page;
        } else {
            if (generate) {
                return _buildVariant(prevPage, source.getInstance(), user.isPageScreenshotEnabled());
            } else {
                return null;
            }
        }
    }

    public PageBean getVariant(Page prevPage, Element source, String hash, boolean generate) throws Exception {
        PageBean page = prevPage.findVariantByHash(hash);
        if (page == null) {
            if (generate) {
                page = buildHashVariant(prevPage, source, hash);
            }
        }
        return page;
    }

    private Page _buildVariant(Page page, Element source, boolean takeScreenshot) throws Exception {
        Page variant = new Page(user, page.getUrl());
        variant.setOriginal(page.getOriginal());
        variant.setSource(source);
        if (takeScreenshot) {
            new PageParser(variant).takeScreenShot();
        }
        return variant;
    }

    private PageBean buildHashVariant(Page prevPage, Element source, String hash) throws Exception {
        Page variant = _buildVariant(prevPage, source, user.isPageScreenshotEnabled());
        variant.setUrl(variant.getUrl() + "#" + hash);

        return variant.getBean();
    }

    private SiteMap getSiteMap() {
        return user.getSiteMap();
    }

    private Application getApplication() {
        return user.getApplication();
    }

    private WebDriver getBrowser() throws Exception {
        return user.getBrowser();
    }

}