org.suren.autotest.web.framework.settings.Phoenix.java Source code

Java tutorial

Introduction

Here is the source code for org.suren.autotest.web.framework.settings.Phoenix.java

Source

/*
 *
 *  * Copyright 2002-2007 the original author or authors.
 *  *
 *  * Licensed 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.suren.autotest.web.framework.settings;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import org.dom4j.xpath.DefaultXPath;
import org.jaxen.SimpleNamespaceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.suren.autotest.web.framework.AutoApplicationConfig;
import org.suren.autotest.web.framework.annotation.AutoApplication;
import org.suren.autotest.web.framework.hook.ShutdownHook;
import org.suren.autotest.web.framework.selenium.SeleniumEngine;
import org.suren.autotest.web.framework.spring.AutoModuleScope;
import org.suren.autotest.web.framework.util.BeanUtil;
import org.suren.autotest.web.framework.validation.Validation;
import org.xml.sax.SAXException;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.JCommander.Builder;
import com.surenpi.autotest.datasource.ClasspathResource;
import com.surenpi.autotest.datasource.DataResource;
import com.surenpi.autotest.datasource.DataSource;
import com.surenpi.autotest.datasource.DynamicDataSource;
import com.surenpi.autotest.datasource.FileResource;
import com.surenpi.autotest.report.RecordReportWriter;
import com.surenpi.autotest.report.ReportStore;
import com.surenpi.autotest.report.record.ProjectRecord;
import com.surenpi.autotest.utils.NetUtil;
import com.surenpi.autotest.utils.StringUtils;
import com.surenpi.autotest.webui.Page;
import com.surenpi.autotest.webui.core.AutoTestException;
import com.surenpi.autotest.webui.core.ConfigException;
import com.surenpi.autotest.webui.core.ConfigNotFoundException;
import com.surenpi.autotest.webui.core.PageContext;
import com.surenpi.autotest.webui.core.PageContextAware;
import com.surenpi.autotest.webui.core.WebUIEngine;

import net.sf.json.util.JSONUtils;

/**
 * ?page???
 * @author <a href="http://surenpi.com">suren</a>
 * @since Jul 17, 2016 9:01:51 AM
 */
public class Phoenix implements Closeable, WebUIEngine {
    private static final Logger logger = LoggerFactory.getLogger(Phoenix.class);

    private Map<String, Page> pageMap = new HashMap<String, Page>();
    private Map<String, DataSourceInfo> dataSourceMap = new HashMap<String, DataSourceInfo>();
    private Map<String, DynamicDataSource> dynamicDataSourceMap;
    private ApplicationContext context;
    private ShutdownHook shutdownHook;
    private Class<?>[] annotatedClasses;

    /** ? */
    private File configFile;

    private boolean closed = false;

    /** ??Page? */
    private Set<String> excludePageSet = new HashSet<String>();

    /**
     * ?bean?JMX????
     */
    public Phoenix(Class<?>... annotatedClasses) {
        this.annotatedClasses = annotatedClasses;
    }

    /**
     * auto??
     */
    private void annotationScan() {
        SeleniumEngine engine = getEngine();

        AnnotationProcess annotationProcess = new AnnotationProcess(this.context, this.dataSourceMap);
        annotationProcess.scan(pageMap, engine);
    }

    /**
     * ?
     * 
     * @param filePath filePath
     * @throws DocumentException DocumentException
     * @throws IOException IOException
     * @throws SAXException SAXException
     */
    public void read(String filePath) throws DocumentException, IOException, SAXException {
        File file = new File(filePath);
        try (FileInputStream fis = new FileInputStream(file)) {
            read(fis);
        }
    }

    /**
     * ??
     * 
     * @param fileName fileName
     * @throws IOException IOException
     * @throws DocumentException xml?
     * @throws SAXException xml?
     */
    public void readFromClassPath(String fileName) throws IOException, DocumentException, SAXException {
        ClassLoader classLoader = this.getClass().getClassLoader();

        try (InputStream inputStream = classLoader.getResourceAsStream(fileName)) {
            if (inputStream == null) {
                throw new ConfigNotFoundException("ClassPath", fileName);
            }

            Validation.validationFramework(inputStream); //?
        } catch (SAXException | IOException e) {
            logger.error("Framework validation process error.", e);
            throw e;
        }

        //??
        try (InputStream confInput = classLoader.getResourceAsStream(fileName)) {
            read(confInput);
        } catch (DocumentException | IOException e) {
            logger.error(String.format("Main config [%s] parse process error.", fileName), e);
            throw e;
        }
    }

    /**
     * ??
     * @param filePath filePath
     * @throws IOException  IOException
     * @throws FileNotFoundException  FileNotFoundException
     * @throws DocumentException  DocumentException
     * @throws SAXException SAXException
     */
    public void readFromSystemPath(String filePath)
            throws FileNotFoundException, IOException, DocumentException, SAXException {
        configFile = new File(filePath);
        if (!configFile.isFile()) {
            throw new ConfigException(String.format("Target file [%s] is not a file.", filePath), "");
        } else if (!filePath.endsWith(".xml")) {
            logger.warn("Target file [%s] is not end with .xml", filePath);
        }

        try (InputStream configInput = new FileInputStream(configFile)) {
            read(configInput);
        }
    }

    /**
     * ???
     * 
     * @param inputStream inputStream
     * @throws DocumentException DocumentException
     * @throws IOException IOException
     * @throws SAXException SAXException
     */
    public void read(InputStream inputStream) throws DocumentException, IOException, SAXException {
        Document document = new SAXReader().read(inputStream);

        parse(document);
    }

    /**
     * @param params
     * @return
     */
    public SeleniumEngine init(String[] params) {
        PhoenixParam phoenixParam = new PhoenixParam();
        Builder builder = JCommander.newBuilder();
        JCommander commander = builder.addObject(phoenixParam).build();

        if (params != null) {
            commander.parse(params);
        }

        if (phoenixParam.printUsage) {
            commander.usage();
            System.exit(0);
        }

        context = SpringUtils.getApplicationContext();
        if (context == null || !((AbstractApplicationContext) context).isActive()) {
            if (annotatedClasses == null) {
                annotatedClasses = new Class[] { AutoApplicationConfig.class };
            } else {
                int len = annotatedClasses.length;
                annotatedClasses = Arrays.copyOf(annotatedClasses, len + 1);
                annotatedClasses[len] = AutoApplicationConfig.class;
            }
            context = new AnnotationConfigApplicationContext(annotatedClasses);
            //          ((AnnotationConfigApplicationContext) context).getBeanFactory().registerScope("autotest", new AutotestScope());
            Map<String, RecordReportWriter> reportWriters = context.getBeansOfType(RecordReportWriter.class);

            ((AnnotationConfigApplicationContext) context).getBeanFactory().registerScope("module",
                    new AutoModuleScope(reportWriters.values().parallelStream().collect(Collectors.toList()),
                            this));

            // 
            if (StringUtils.isNotBlank(phoenixParam.reportStore)) {
                Map<String, ReportStore> reportStores = context.getBeansOfType(ReportStore.class);
                reportStores.forEach((name, store) -> {
                    store.setStoreRoot(phoenixParam.reportStore);
                });
            }
        }

        //auto??
        annotationScan();

        try {
            //?
            Map<String, PageContextAware> pageContextAwareList = context.getBeansOfType(PageContextAware.class);
            if (pageContextAwareList != null) {
                for (PageContextAware ware : pageContextAwareList.values()) {
                    ware.setPageContext(new PageContext(pageMap));
                }
            }

            //????
            //          IPageMXBean pageMXBean = context.getBean(IPageMXBean.class);
            //          
            //          LocateRegistry.createRegistry(5006);
            //          MBeanServer server = ManagementFactory.getPlatformMBeanServer();
            //          server.registerMBean(pageMXBean,
            //                  new ObjectName("org.suren.autotest.web.framework:type=IPageMXBean"));
        } catch (Exception e) {
            logger.error("jmx register process error.", e);
        }

        shutdownHook = new ShutdownHook(this);
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        Map<String, Object> autoApp = context.getBeansWithAnnotation(AutoApplication.class);
        final String projectName;
        final String projectDesc;
        if (autoApp != null && autoApp.size() > 0) {
            Object appObj = autoApp.values().iterator().next();
            AutoApplication autoApplication = appObj.getClass().getAnnotation(AutoApplication.class);
            if (autoApplication == null) {
                autoApplication = appObj.getClass().getSuperclass().getAnnotation(AutoApplication.class);
            }
            projectName = autoApplication.name();
            projectDesc = autoApplication.description();
        } else {
            projectName = "none";
            projectDesc = "none";
        }

        Map<String, RecordReportWriter> writers = context.getBeansOfType(RecordReportWriter.class);
        if (writers != null) {
            writers.forEach((name, writer) -> {
                ProjectRecord projectRecord = new ProjectRecord();
                projectRecord.setName(projectName);
                projectRecord.setDescription(projectDesc);
                projectRecord.setBrowserInfo("browserInfo");
                projectRecord.setOsName(System.getProperty("os.name"));
                projectRecord.setOsArch(System.getProperty("os.arch"));
                projectRecord.setOsVersion(System.getProperty("os.version"));
                projectRecord.setCountry(System.getProperty("user.country"));
                projectRecord.setLanguage(System.getProperty("user.language"));
                projectRecord.setTimezone(System.getProperty("user.timezone"));
                projectRecord.setAddressInfo(JSONUtils.valueToString(NetUtil.allIP()));
                writer.write(projectRecord);
            });
        }

        logger.info("init process done.");

        SeleniumEngine engine = getEngine();
        if (StringUtils.isNotBlank(phoenixParam.browser)) {
            engine.setDriverStr(phoenixParam.browser);
        }
        if (StringUtils.isNotBlank(phoenixParam.remote)) {
            engine.setRemoteStr(phoenixParam.remote);
        }

        if (engine.getDriver() != null) {
            engine.init();
        }

        return engine;
    }

    /**
     * ?
     * @return 
     */
    public SeleniumEngine init() {
        return init(null);
    }

    /**
     * ????
     * @see #init()
     * @see #initData()
     */
    public void initWithData() {
        init();
        initData();
    }

    /**
     * ???page
     * @see #initData(int)
     */
    public void initData() {
        initData(1);
    }

    /**
     * ???page
     * @param row ???1
     * @return ??
     */
    public List<DynamicDataSource> initData(int row) {
        List<DynamicDataSource> dynamicDataSourceList = new ArrayList<DynamicDataSource>();
        Iterator<String> pageIterator = pageMap.keySet().iterator();
        while (pageIterator.hasNext()) {
            String pageKey = pageIterator.next();
            if (containExcludePage(pageKey)) {
                logger.warn(
                        String.format("Page [%s] has been exclude, " + "ignore for init data [%s]!", pageKey, row));
                continue;
            }

            Page page = pageMap.get(pageKey);

            DynamicDataSource dynamicDataSource = initPageData(page, row);
            if (dynamicDataSource != null) {
                dynamicDataSourceList.add(dynamicDataSource);
            }
        }

        return dynamicDataSourceList;
    }

    /**
     * ???Page
     * @param page ?
     * @param row ???1
     * @return ??
     */
    public DynamicDataSource initPageData(Page page, int row) {
        String dataSourceStr = page.getDataSource();
        if (StringUtils.isBlank(dataSourceStr)) {
            return null;
        }

        final DataSourceInfo dataSourceInfo = dataSourceMap.get(dataSourceStr);
        if (dataSourceInfo == null) {
            return null;
        }

        getDynamicDataSources();
        @SuppressWarnings({ "unchecked", "rawtypes" })
        DataSource<Page> dataSource = (DataSource) dynamicDataSourceMap.get(dataSourceInfo.getType());
        if (dataSource == null) {
            throw new AutoTestException("Can not found dataSource by type : " + dataSourceInfo.getType());
        }

        String resoucePathName = dataSourceInfo.getResource();
        DataResource clzResource = new ClasspathResource(Phoenix.class, resoucePathName);
        try {
            if (clzResource.getUrl() == null) {
                if (configFile != null) {
                    File file = new File(configFile.getParentFile(), dataSourceInfo.getResource());
                    if (!file.isFile()) {
                        throw new ConfigNotFoundException("dataSourceFile", file.getAbsolutePath());
                    }

                    clzResource = new FileResource(file);
                } else {
                    throw new ConfigNotFoundException(
                            String.format("Can not found dataSource file '%s' from classpath.", resoucePathName));
                }
            }
        } catch (IOException e) {
            logger.error("", e);
        }

        dataSource.loadData(clzResource, row, page);

        return dataSource;
    }

    /**
     * @return ??
     */
    public Collection<DynamicDataSource> getDynamicDataSources() {
        if (dynamicDataSourceMap == null) {
            dynamicDataSourceMap = context.getBeansOfType(DynamicDataSource.class);
        }

        if (dynamicDataSourceMap != null) {
            return dynamicDataSourceMap.values();
        } else {
            return null;
        }
    }

    /**
     * Page??
     * @param pageCls pageCls
     */
    public void addExcludePage(String pageCls) {
        excludePageSet.add(pageCls);
    }

    /**
     * ?Page?
     * @param pageCls pageCls
     */
    public void removeExcludePage(String pageCls) {
        excludePageSet.remove(pageCls);
    }

    /**
     * ???Page?
     * @param pageCls pageCls
     * @return pageCls
     */
    public boolean containExcludePage(String pageCls) {
        return excludePageSet.contains(pageCls);
    }

    /**
     * ?Page?
     */
    public void clearExcludePage() {
        excludePageSet.clear();
    }

    /**
     * @return 
     */
    public SeleniumEngine getEngine() {
        return context.getBean(SeleniumEngine.class);
    }

    /**
     * ??
     * @param doc xml
     * @throws DocumentException  xml?
     * @throws IOException ??
     * @throws SAXException ?? 
     */
    private void parse(Document doc) throws IOException, DocumentException, SAXException {
        SimpleNamespaceContext simpleNamespaceContext = new SimpleNamespaceContext();
        simpleNamespaceContext.addNamespace("ns", "http://surenpi.com");

        SeleniumEngine seleniumEngine = getEngine();
        if (StringUtils.isBlank(seleniumEngine.getDriverStr())) {
            XPath xpath = new DefaultXPath("/ns:autotest/ns:engine");
            xpath.setNamespaceContext(simpleNamespaceContext);
            // engine parse progress
            Element engineEle = (Element) xpath.selectSingleNode(doc);
            if (engineEle == null) {
                throw new RuntimeException("can not found engine config.");
            }

            String driverStr = engineEle.attributeValue("driver", "chrome");
            String remoteStr = engineEle.attributeValue("remote");
            String timeOutStr = engineEle.attributeValue("timeout");
            String fullScreenStr = engineEle.attributeValue("fullScreen", "false");
            String maximizeStr = engineEle.attributeValue("maximize", "true");
            String widthStr = engineEle.attributeValue("width");
            String heightStr = engineEle.attributeValue("height");
            try {
                seleniumEngine.setDriverStr(driverStr);
                seleniumEngine.setRemoteStr(remoteStr);

                try {
                    seleniumEngine.setTimeout(Long.parseLong(timeOutStr));
                } catch (NumberFormatException e) {
                    logger.warn(String.format("Invalid number string [%s].", timeOutStr));
                    seleniumEngine.setTimeout(5);
                }

                try {
                    seleniumEngine.setWidth(Integer.parseInt(widthStr));
                    seleniumEngine.setHeight(Integer.parseInt(heightStr));
                } catch (NumberFormatException e) {
                    logger.warn(String.format("Invalid number width [%s] or height [%s].", widthStr, heightStr));
                }

                seleniumEngine.setFullScreen(Boolean.parseBoolean(fullScreenStr));
                seleniumEngine.setMaximize(Boolean.parseBoolean(maximizeStr));

                seleniumEngine.init();
            } catch (NoSuchBeanDefinitionException e) {
                logger.error("Can not found bean SeleniumEngine.", e);
            }
        }

        XPath xpath = new DefaultXPath("/ns:autotest/ns:includePage");
        xpath.setNamespaceContext(simpleNamespaceContext);
        @SuppressWarnings("unchecked")
        List<Element> includePageList = xpath.selectNodes(doc);
        if (includePageList != null && includePageList.size() > 0) {
            for (Element includePage : includePageList) {
                String pageConfig = includePage.attributeValue("pageConfig");

                readFromClassPath(pageConfig);
            }
        }

        xpath = new DefaultXPath("/ns:autotest/ns:pages");
        xpath.setNamespaceContext(simpleNamespaceContext);
        Element pagesEle = (Element) xpath.selectSingleNode(doc);
        String pagePackage = pagesEle.attributeValue("pagePackage", "");
        if (StringUtils.isNotBlank(pagePackage)) {
            pagePackage = (pagePackage.trim() + ".");
        }

        // pages parse progress
        xpath = new DefaultXPath("/ns:autotest/ns:pages/ns:page");
        xpath.setNamespaceContext(simpleNamespaceContext);
        @SuppressWarnings("unchecked")
        List<Element> pageNodes = xpath.selectNodes(doc);
        if (pageNodes != null) {
            for (Element ele : pageNodes) {
                String pageClsStr = ele.attributeValue("class");
                if (pageClsStr == null) {
                    logger.warn("can not found class attribute.");
                    continue;
                }

                pageClsStr = (pagePackage + pageClsStr);
                String dataSrcClsStr = ele.attributeValue("dataSource");

                try {
                    parse(pageClsStr, dataSrcClsStr, ele);
                } catch (NoSuchBeanDefinitionException e) {
                    logger.error("Page element [{}] parse error, in document [{}].", pageClsStr, doc);
                    throw e;
                } catch (Exception e) {
                    logger.error("Page element parse error.", e);
                }
            }
        }

        //parse datasources
        xpath = new DefaultXPath("/ns:autotest/ns:dataSources/ns:dataSource");
        xpath.setNamespaceContext(simpleNamespaceContext);
        @SuppressWarnings("unchecked")
        List<Element> dataSourceNodes = xpath.selectNodes(doc);
        if (dataSourceNodes != null) {
            for (Element ele : dataSourceNodes) {
                String name = ele.attributeValue("name");
                String type = ele.attributeValue("type");
                String resource = ele.attributeValue("resource");

                dataSourceMap.put(name, new DataSourceInfo(type, resource));
            }
        }
    }

    /**
     * ??Page
     * 
     * @param pageClsStr pageClsStr
     * @param dataSrcClsStr dataSrcClsStr
     * @param ele ele
     */
    private void parse(final String pageClsStr, String dataSrcClsStr, Element ele) throws Exception {
        final Object pageInstance = getBean(pageClsStr);

        String url = ele.attributeValue("url");
        if (url != null) {
            BeanUtil.set(pageInstance, "url", url);
        }
        String paramPrefix = ele.attributeValue("paramPrefix", "param");
        BeanUtil.set(pageInstance, "paramPrefix", paramPrefix);

        BeanUtil.set(pageInstance, "dataSource", dataSrcClsStr);

        FieldVisitor fieldVisitor = new FieldVisitor(pageInstance, pageClsStr, context);
        ele.accept(fieldVisitor);

        pageMap.put(pageClsStr, (Page) pageInstance);
    }

    /**
     * ??
     * @param pageClsStr pageClsStr
     * @return pageClsStr
     */
    private Object getBean(String pageClsStr) {
        String beanName = convertBeanName(pageClsStr);

        Object obj = null;

        try {
            obj = context.getBean(beanName);
        } catch (NoSuchBeanDefinitionException e) {
            obj = context.getBean(pageClsStr);
        }

        return obj;
    }

    /**
     * ??bean??
     * @param pageClsStr pageClsStr
     * @return pageClsStr
     */
    private String convertBeanName(final String pageClsStr) {
        String result = pageClsStr;
        int index = pageClsStr.lastIndexOf(".");

        if (index < 0) {
            result = pageClsStr;
        } else {
            result = pageClsStr.substring(index + 1);
        }

        if (result.length() > 1 && result.charAt(1) >= 'A' && result.charAt(1) <= 'Z') {
            return result;
        }

        return StringUtils.uncapitalize(result);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getDriver() {
        return (T) getEngine().getDriver();
    }

    /**
     * @param name name
     * @return ??Page
     */
    public Object getPage(String name) {
        return pageMap.get(name);
    }

    /**
     * @param type type
     * @return Page
     */
    @SuppressWarnings("unchecked")
    @Deprecated
    public <T> T getPage(T type) {
        return (T) getPage(type.getClass().getName());
    }

    /**
     * @param type type
     * @return Page
     */
    @SuppressWarnings("unchecked")
    @Override
    public <T> T getPage(Class<T> type) {
        return (T) getPage(type.getName());
    }

    @Override
    public <T> T getModule(Class<T> type) {
        return context.getBean(type);
    }

    @Override
    public <T> T getForm(Class<T> form) {
        return context.getBean(form);
    }

    @Override
    public void open(String url) {
        getEngine().getDriver().get(url);
    }

    /**
     * 
     */
    @Override
    public void close() throws IOException {
        if (closed) {
            return;
        }

        SeleniumEngine engine = context.getBean(SeleniumEngine.class);
        if (engine != null) {
            engine.close();
            closed = true;

            Runtime.getRuntime().removeShutdownHook(shutdownHook);
        } else {
            logger.error("Can not fond seleniumEngine, resource close failed.");
        }
    }

    /**
     * 
     */
    public void shutdown() {
        ((AbstractApplicationContext) context).destroy();
        ((AbstractApplicationContext) context).close();
    }

    /**
     * @return ??
     */
    public boolean isClosed() {
        return closed;
    }
}