java解析PDM中的表和字段以及类型

PDM的项目文件其实都是XML文档,所以只要找到对应的标签就能找到对应的表和字段,解析XML文件我们采用的dom4j的xpath查找.
具体代码实现如下:

public class PDMHelper {
    public static final String TABLES_TAG = "c:Tables";
    public static final String TABLE_TAG = "o:Table";
    public static final String TABLE_NAME_TAG = "a:Code";
    public static final String TABLE_DESC_TAG = "a:Name";
    public static final String COLUMNS_TAG = "c:Columns";
    public static final String COLUMN_TAG = "o:Column";
    public static final String COLUMN_NAME_TAG = "a:Code";
    public static final String COLUMN_DESC_TAG = "a:Name";
    public static final String COLUMN_DATA_TYPE_TAG = "a:DataType";
    public static final String COLUMN_DATA_LEN_TAG = "a:Length";
    public static final String COLUMN_MANDATORY_TAG = "a:Column.Mandatory";
	public static void parsePDM(String file) throws Exception {
		SAXReader reader = new SAXReader();
		Document document = reader.read(new File(file));
		List<Node> tables = document.selectNodes("//" + TABLES_TAG + "/" + TABLE_TAG);
		for (Node table : tables) {
			Node tableName = table.selectSingleNode(TABLE_NAME_TAG);
			Node tableDesc = table.selectSingleNode(TABLE_DESC_TAG);
			System.out.println(tableName.getText() + "\t" + tableDesc.getText());
			List<Node> columns = table.selectNodes(COLUMNS_TAG + "/" + COLUMN_TAG);
			for (Node column : columns) {
				Node columnName = column.selectSingleNode(COLUMN_NAME_TAG);
				Node columnDesc = column.selectSingleNode(COLUMN_DESC_TAG);
				Node dataType = column.selectSingleNode(COLUMN_DATA_TYPE_TAG);
				//Node dataLength = column.selectSingleNode("./" + COLUMN_DATA_LEN_TAG);
				System.out.println("\t\t" + columnName.getText() + "\t" 
				+ dataType.getText() + "\t" + columnDesc.getText());
			}
		}
	}
}

vsftp关于"550 create directory operation failed"

配置好vsftp, 能登录浏览,但是不可以新建文件/文件夹, 删除文件, 重命名等,并且报错:"550 create directory operation failed". 把服务器ftp文件目录全部改成777权限还是不可以.正确的解决方法是关闭linux的SElinux.

具体配置如下:
修改/etc/sysconfig/selinux或者/etc/selinux/config文件:

#SELINUX=enforcing
SELINUX=disabled

修改后要想生效可以重启,也可以不重启应用配置,用命令:

setenforce 0

查看Selinux状态:

# getenforce
Permissive

SELinux是「Security-Enhanced Linux」的简称,是美国国家安全局「NSA=The National Security Agency」 和SCC(Secure Computing Corporation)开发的 Linux的一个扩张强制访问控制安全模块。原先是在Fluke上开发的,2000年以 GNU GPL 发布。SELinux是一种基于域-类型模型(domain-type)的强制访问控制(MAC)安全系统,它由NSA编写并设计成内核模块包含到内核中,相应的某些安全相关的应用也被打了SELinux的补丁,最后还有一个相应的安全策略。SELinux 是 2.6 版本的 Linux 内核中提供的强制访问控制

Maven中使用APT

APT(Annotation Processing Tool)是一个java注解处理工具,主要运行在JDK编译阶段,我们可以简单理解为预编译的处理工具类, 就是我们在执行我们传统的编译前,做一些预处理,可以是资源文件的,也可以是JAVA类的,把一部分注解在编译时解释为传统的类,以提高运行时效率,在运行时,这些注解就不要再执行,即使我们使用字节码优化也不能达到这种类似于原生代码的效率.

如果使用APT,建议使用JDK的最小版本为1.6. APT从在JDK 1.5开始支持,但是从1.6版本开始才稳定和可用性有所提高,简化开发部署的难度.
以下所有的阐述都是针对1.6版本以后.

JDK 1.6版本后添加了几个javac参数:

  -proc:{none,only}          Control whether annotation processing and/or compilation is done.
  -processor <class1>[,<class2>,<class3>...]
							 Names of the annotation processors to run; 
							 bypasses default discovery process
  -processorpath <path>      Specify where to find annotation processors
  -s <directory>             Specify where to place generated source files
  -implicit:{none,class}     Specify whether or not to generate class files 
							 for implicitly referenced files
  -Akey[=value]              Options to pass to annotation processors

那么我们在maven工程中只要添加javac参数就可以了.如下示例:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-compiler-plugin</artifactId>
	<version>2.3.2</version>
	<configuration>
		<proc>only</proc>
		<annotationProcessors>
			<annotationProcessor>com.annotation.processor.AnnotationProcessor</annotationProcessor>
		</annotationProcessors>
		<source>1.6</source>
		<target>1.6</target>
	</configuration>
</plugin>

除了以上方法,也有一些插件来处理,如apt-maven-plugin,不过主要是针对eclipse的m2e插件的,具体代码如下:

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/java</outputDirectory>
                <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

Dubbo使用总结

Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。

其核心部分包含:

  • 远程通讯 : 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。
  • 集群容错 : 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
  • 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。

Dubbo能做什么?

  • 透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
  • 软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
  • 服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。

上面是官方对于Dubbo一些描述, 对于稳定性是经过阿里巴巴验证过的,所以稳定是能够得到保证的.
对于不同公司和不同的团队,有着各种需求和情况,那么我们就要探索如何使用Dubbo,使Dubbo能够很好的和我们的目标一致.

应该怎么使用dubbo?

  • 从零开始
    从头开始使用dubbo是比较简单的,我们可以完全按照dubbo官方指导实现. 具体项目示例请参照https://github.com/alibaba/dubbo/tree/master/dubbo-demo
  • 已有项目改造
    如果我们是spring的项目,那么我们可以平滑的过度到dubbo, 具体的实现思路是我们在编译阶段提取我们spring的service,生成consumer和provider文件配置,然后通过启动dubbo来加载我们的服务

接下来我们重点讲述如何对于现有spring项目改造成dubbo
首先如果我们使用dubbo远程服务调用,那么我们如何保证事务? 如果要保证事务,我们需要把所要运新的服务在一台机器执行, 即官方所谓Best Efforts 1PC模式.那我们就要保证暴露的服务尽量是一个事务.
改造的思路是通过约定开发,达到开发和部署分离.开发采用传统的开发方式,在服务器发布时自动提取service,具体实现通过使用java的APT来实现.具体实现已经实现,后面有时间把具体的实现放到github上.

dubbo如何启动?
java com.alibaba.dubbo.container.Main

dubbo如何停止?
优雅停机,Dubbo是通过JDK的ShutdownHook来完成优雅停机的,所以如果用户使用"kill -9 PID"等强制关闭指令,是不会执行优雅停机的,只有通过"kill PID"时,才会执行。

POI根据Excel模板导出大数据

在一些项目中,需要导出数据, 如果编程实现POI样式等比较麻烦,所以一般根据模板来导出数据,之前使用过JXLS来实现,对于小数据比较好用,但是对于数据量比较大就比较慢,甚至死掉,不能导出.

经过一段时间研究POI,发现POI的SXSSF支持大数据导出,经过测试有很好的性能.实现有两种思路:

  • 通过Excel文件copy样式等, 经过长时间验证和测试,有缺陷,会丢掉一些样式,不能完美达到目标
  • 通过修改模板文件,然后直接输出,经过测试验证,这种能够很好的解决大数据导出问题.

下面我们给出第二种思路的具体实现,经过测试导出5W条数据大概需要4S,这个测试结果非常快. 如果使用标准POI XSSF来实现,会卡死不会输出.

/**
     * 根据Excel模板导出数据,支持大数据导出
     *
     * @param templateInputStream
     * @param data
     * @param outputStream
     * @param <T>
     * @throws IOException
     * @throws InvalidFormatException
     */
public static <T> void generateExcel(InputStream templateInputStream, 
Iterator<T> data, OutputStream outputStream) throws IOException, InvalidFormatException {
	// 通过类加载器获取模板
	XSSFWorkbook workbook = new XSSFWorkbook(templateInputStream);
	XSSFSheet sheet = workbook.getSheetAt(0);
	XSSFRow row = sheet.getRow(1);

	List<CellStyle> cellStyles = new ArrayList<CellStyle>();
	List<String> cellValues = new ArrayList<String>();
	Map<Integer, String> cellFormatMap = new HashMap<Integer, String>();
	Pattern pattern = Pattern.compile("^\\$\\{([\\w\\.]+)\\}$");
	for (int i = 0; i < row.getLastCellNum(); i++) {
		XSSFCell cell = row.getCell(i);
		cellStyles.add(cell.getCellStyle());
		String cellValue = cell.getStringCellValue();
		cellValues.add(cellValue);
		if (cellValue != null) {
			Matcher matcher = pattern.matcher(cellValue);
			if (matcher.find()) {
				cellFormatMap.put(i, matcher.group(1));
			}
		}
	}

	sheet.removeRow(row);
	SXSSFWorkbook newWorkbook = new SXSSFWorkbook(workbook);
	Sheet newSheet = newWorkbook.getSheetAt(0);
	int rowNum = 1;
	while (data.hasNext()) {
		T t = data.next();
		JSONObject item = null;
		if (t instanceof JSONObject) {
			item = (JSONObject) t;
		} else {
			String json = JSON.toJSONString(t);
			item = JSON.parseObject(json);
		}

		Row newRow = newSheet.createRow(rowNum);
		for (int i = 0; i < cellValues.size(); i++) {
			Cell newCell = newRow.createCell(i);
			newCell.setCellStyle(cellStyles.get(i));
			String newCellValue = cellValues.get(i);
			if (newCellValue != null && cellFormatMap.containsKey(i)) {
				newCellValue = JsonUtils.getJSONValue(item, cellFormatMap.get(i));
			}
			newCell.setCellValue(newCellValue);
		}

		rowNum++;
	}
	newWorkbook.write(outputStream);
}