Java tutorial
/* * 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.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; 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 org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.VisitorSupport; 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.BeanUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.suren.autotest.web.framework.core.ConfigException; import org.suren.autotest.web.framework.core.ConfigNotFoundException; import org.suren.autotest.web.framework.core.Locator; import org.suren.autotest.web.framework.core.LocatorAware; import org.suren.autotest.web.framework.core.PageContext; import org.suren.autotest.web.framework.core.PageContextAware; import org.suren.autotest.web.framework.core.ui.AbstractElement; import org.suren.autotest.web.framework.core.ui.Text; import org.suren.autotest.web.framework.data.ClasspathResource; import org.suren.autotest.web.framework.data.DataResource; import org.suren.autotest.web.framework.data.DataSource; import org.suren.autotest.web.framework.data.DynamicDataSource; import org.suren.autotest.web.framework.data.FileResource; import org.suren.autotest.web.framework.hook.ShutdownHook; import org.suren.autotest.web.framework.page.Page; import org.suren.autotest.web.framework.selenium.SeleniumEngine; import org.suren.autotest.web.framework.util.BeanUtil; import org.suren.autotest.web.framework.util.StringUtils; import org.suren.autotest.web.framework.validation.Validation; import org.xml.sax.SAXException; /** * ?page??? * @author suren * @date Jul 17, 2016 9:01:51 AM */ public class SettingUtil implements Closeable { private static final Logger logger = LoggerFactory.getLogger(SettingUtil.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 File configFile; private boolean closed = false; /** ??Page? */ private Set<String> excludePageSet = new HashSet<String>(); /** * ?bean?JMX???? */ public SettingUtil() { context = SpringUtils.getApplicationContext(); if (context == null || !((AbstractApplicationContext) context).isActive()) { context = new ClassPathXmlApplicationContext(new String[] { "classpath*:autoTestContext.xml", "classpath*:applicationContext.xml", "classpath*:beanScope.xml" }); } 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); logger.info("init process done."); } /** * ? * * @param filePath * @throws DocumentException * @throws IOException * @throws 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 * @throws 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 * @throws IOException * @throws FileNotFoundException * @throws DocumentException * @throws 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 * @throws DocumentException * @throws IOException * @throws SAXException */ public void read(InputStream inputStream) throws DocumentException, IOException, SAXException { Document document = new SAXReader().read(inputStream); parse(document); } /** * ???page * @see {@link #initData(int)} */ public void initData() { initData(1); } /** * ???page * @param ???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(); DataSource dataSource = (DataSource) dynamicDataSourceMap.get(dataSourceInfo.getType());//context.getBean(dataSourceInfo.getType(), DataSource.class); DataResource clzResource = new ClasspathResource(SettingUtil.class, dataSourceInfo.getResource()); try { if (clzResource.getUrl() == null) { File file = new File(configFile.getParentFile(), dataSourceInfo.getResource()); if (!file.isFile()) { throw new RuntimeException("Can not found datasource file : " + file.getAbsolutePath()); } clzResource = new FileResource(file); } } catch (IOException e) { e.printStackTrace(); } dataSource.loadData(clzResource, row, page); return dataSource; } public Collection<DynamicDataSource> getDynamicDataSources() { if (dynamicDataSourceMap == null) { dynamicDataSourceMap = context.getBeansOfType(DynamicDataSource.class); } if (dynamicDataSourceMap != null) { return dynamicDataSourceMap.values(); } else { return null; } } /** * Page?? * @param pageCls */ public void addExcludePage(String pageCls) { excludePageSet.add(pageCls); } /** * ?Page? * @param pageCls */ public void removeExcludePage(String pageCls) { excludePageSet.remove(pageCls); } /** * ???Page? * @param pageCls * @return */ 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 document * @throws DocumentException * @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 * @param dataSrcClsStr * @param ele */ private void parse(final String pageClsStr, String dataSrcClsStr, Element ele) throws Exception { final Object pageInstance = getBean(pageClsStr); final Class<?> pageCls = pageInstance.getClass(); 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); ele.accept(new VisitorSupport() { @Override public void visit(Element node) { if (!"field".equals(node.getName())) { return; } String fieldName = node.attributeValue("name"); String byId = node.attributeValue("byId"); String byCss = node.attributeValue("byCss"); String byName = node.attributeValue("byName"); String byXpath = node.attributeValue("byXpath"); String byLinkText = node.attributeValue("byLinkText"); String byPartialLinkText = node.attributeValue("byPartialLinkText"); String byTagName = node.attributeValue("byTagName"); String data = node.attributeValue("data"); String type = node.attributeValue("type"); String strategy = node.attributeValue("strategy"); String paramPrefix = node.attributeValue("paramPrefix", "param"); String timeOut = node.attributeValue("timeout", "0"); if (fieldName == null || "".equals(fieldName)) { return; } AbstractElement ele = null; try { Method getterMethod = BeanUtils.findMethod(pageCls, "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1)); if (getterMethod != null) { ele = (AbstractElement) getterMethod.invoke(pageInstance); if (ele == null) { logger.error( String.format("element [%s] is null, maybe you not set autowired.", fieldName)); return; } if ("input".equals(type)) { Text text = (Text) ele; text.setValue(data); ele = text; } ele.setId(byId); ele.setName(byName); ele.setTagName(byTagName); ele.setXPath(byXpath); ele.setCSS(byCss); ele.setLinkText(byLinkText); ele.setPartialLinkText(byPartialLinkText); ele.setStrategy(strategy); ele.setParamPrefix(paramPrefix); ele.setTimeOut(Long.parseLong(timeOut)); } else { logger.error(String.format("page cls [%s], field [%s]", pageClsStr, fieldName)); } } catch (IllegalAccessException e) { logger.error(e.getMessage(), e); } catch (IllegalArgumentException e) { logger.error(e.getMessage(), e); } catch (InvocationTargetException e) { logger.error(e.getMessage(), e); } catch (ClassCastException e) { logger.error(String.format("fieldName [%s]", fieldName), e); } node.accept(new FieldLocatorsVisitor(ele)); } }); pageMap.put(pageClsStr, (Page) pageInstance); } /** * ?? * @param pageClsStr * @return */ 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 * @return */ 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); } /** * ??? * @author suren * @date 2016728 ?8:18:01 */ class FieldLocatorsVisitor extends VisitorSupport { private AbstractElement absEle; public FieldLocatorsVisitor(AbstractElement element) { this.absEle = element; } @Override public void visit(Element node) { if (!"locator".equals(node.getName())) { return; } String name = node.attributeValue("name"); String value = node.attributeValue("value"); String timeoutStr = node.attributeValue("timeout"); String extend = node.attributeValue("extend"); if (StringUtils.isBlank(name) || StringUtils.isBlank(value)) { logger.error("locator has empty name or value."); } long timeout = 0; if (StringUtils.isNotBlank(timeoutStr)) { try { timeout = Long.parseLong(timeoutStr); } catch (NumberFormatException e) { } } Map<String, Locator> beans = context.getBeansOfType(Locator.class); Collection<Locator> locatorList = beans.values(); for (Locator locator : locatorList) { if (!name.equals(locator.getType())) { continue; } if (locator instanceof LocatorAware) { LocatorAware locatorAware = (LocatorAware) locator; locatorAware.setValue(value); locatorAware.setTimeout(timeout); locatorAware.setExtend(extend); absEle.getLocatorList().add(locator); break; } } } } /** * @param name * @return ??Page */ public Object getPage(String name) { return pageMap.get(name); } /** * @param type * @return Page */ @SuppressWarnings("unchecked") @Deprecated public <T> T getPage(T type) { return (T) getPage(type.getClass().getName()); } /** * @param type * @return Page */ @SuppressWarnings("unchecked") public <T> T getPage(Class<T> type) { return (T) getPage(type.getName()); } class DataSourceInfo { private String type; private String resource; public DataSourceInfo(String type, String resource) { this.type = type; this.resource = resource; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } } /** * */ @Override public void close() throws IOException { if (closed) { return; } SeleniumEngine engine = context.getBean(SeleniumEngine.class); if (engine != null) { engine.close(); ((AbstractApplicationContext) context).destroy(); ((AbstractApplicationContext) context).close(); closed = true; Runtime.getRuntime().removeShutdownHook(shutdownHook); } else { logger.error("Can not fond seleniumEngine, resource close failed."); } } /** * @return ?? */ public boolean isClosed() { return closed; } }