抽象模型,严谨代码,开源分享_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 抽象模型,严谨代码,开源分享

抽象模型,严谨代码,开源分享

 2014/6/8 18:25:29  threeman  程序员俱乐部  我要评论(0)
  • 摘要:前言首先,感谢Eric对我代码上的建议,感谢Stone在FTPLab环境部署上对我的指导。今年4月份的时候,做了一个小的项目,当时也没有去总结整理,现在想想总结整理是很有必要的,这也是一个很好的工作研究的习惯。关于项目,不论大小,其实做到极致也不是一件容易的事。只有做到极致,才算真正的项目经验;只有做到极致,才能让编程真正成为一门艺术;只有体会编程是一门有趣的艺术时,你的职业生涯才经久不衰,常青不老。当然,我现在也只是一个走在编程艺术道路上的小孩,不停探索,充满好奇
  • 标签:代码 抽象 开源
前言
    首先,感谢Eric对我代码上的建议,感谢Stone在FTP Lab环境部署上对我的指导。

    今年4月份的时候,做了一个小的项目,当时也没有去总结整理,现在想想总结整理是很有必要的,这也是一个很好的工作研究习惯

    关于项目,不论大小,其实做到极致也不是一件容易的事。只有做到极致,才算真正的项目经验;只有做到极致,才能让编程真正成为一门艺术;只有体会编程是一门有趣的艺术时,你的职业生涯才经久不衰,常青不老。

    当然,我现在也只是一个走在编程艺术道路上的小孩,不停探索,充满好奇,我也希望志同道合的同仁们给我指教与分享。

    我相信,只有开源了,才会有更多更大的进步,才会有活力,有创造力。我支持开源与自由,我唾弃陈旧死板的开发与管理!


项目简介
    首先,将Upload上来的大XML文件按ManagedElement数目进行Split,并且保证Split时不能破坏XML文件完整性。Spliter只有一个main thread来处理。

    然后,对拆分后的XML文件进行Convert,Convert的时候会用到XSLT,以此来简化代码逻辑,实现代码业务逻辑与文本处理逻辑的解耦。并且转换输出是按ObjectType存放在文件中的。Converter是线程池管理下的multi-thread来处理。

    由于:
         1)每个ManagedElement下面的ObjectType数目不同;
         2)每类ObjectType所包含的属性项数量也不同。
所以,这必然会造成输出文件大小的不平衡性,所以还要进行Sub-Split。Sub-Spliter是线程池管理下的multi-thread来处理。

    因为,对每类ObjectType输出文件分页计数有严格的要求,同时在Convert,以及再次Sub-Split之后,保证分页是连续的,所以设计了一个对所有ObjectType分页的分页计数器,在多线程环境下是线程安全的,以后将支持分布式环境上的分页计数。

    最后,进入Notify模块,将消息写入消息文件,供其它模块读取。Notifier是线程池管理下的multi-thread来处理。


编程点滴
1. 同样一个文件能只打开一次搞定的,就只打开一次,比如:读一个属性文件,文件中有很多属性项目,我们可以将属性项预定义在一个数组里,当打开属性文件时,遍历属性数组,将得到的属性键值对预存在一个哈希表里,这样之后在用到属性值里,只需从哈希表里获取就可以了。
class="java" name="code">		String[] propertyArray = new String[] { "com.nsn.oss.nbi.cmcc.cm.templateDirectory", "com.nsn.oss.nbi.cmcc.cm.sourceDirectory", "com.nsn.oss.nbi.ftp.export.directory.cm",
				"com.nsn.oss.nbi.ftp.filereadyfile", "com.nsn.oss.nbi.cmcc.cm.xmlCMIRPVersion", "com.nsn.oss.nbi.ftirp.cm.expireHour",
				"com.nsn.oss.nbi.cmcc.cm.splitor.meLimitCount", "com.nsn.oss.nbi.cmcc.cm.fileNamePrefix", "com.nsn.oss.nbi.cmcc.cm.splitor.subObjectLimitCount" };

		Map<String, String> propertyMap = CommonTool.getPropertyMap(ConstantPool.CM_CONFIGRATION_FILE, propertyArray);


	public static Map<String, String> getPropertyMap(String cmccCmConfigurationFile, String[] propertyArray) {
		InputStream inputStream = null;

		Map<String, String> propertyMap = new HashMap<String, String>();
		int propertyArrayLength = propertyArray.length;

		Properties properties = new Properties();

		try {
			inputStream = Files.newInputStream(Paths.get(cmccCmConfigurationFile));
			properties.load(inputStream);
		} catch (IOException ioe) {
			LOG.error(ioe.getMessage());
			ioe.printStackTrace();
		} finally {
			IOUtils.closeQuietly(inputStream);
		}

		for (int index = 0; index < propertyArrayLength; index++) {
			String propertyName = propertyArray[index];
			String propertyValue = properties.getProperty(propertyName);

			if (StringUtils.trimToEmpty(propertyValue).equals("")) {
				throw new NullPointerException("There is no property " + propertyName + " in the configuration file " + cmccCmConfigurationFile);
			} else {
				propertyValue = StringUtils.trim(propertyValue);
			}

			propertyMap.put(propertyName, propertyValue);
		}

		return propertyMap;
	}


2. 我们能计算一次搞定的,就只计算一次,比如:我们会经常遍历数组,往往会让数组长度的计算在循环中计算多次,这是低效的。
		int propertyArrayLength = propertyArray.length;

		for (int index = 0; index < propertyArrayLength; index++) {
			......
		}


		for (int index = 0; index < propertyArray.length; index++) {
			......
		}


3. 我们处理属性文件时,一定要严谨,比如:有个属性项是表示数量的,当我们获取到该属性项时,我们要对其进行数字正则判断,要不然你得到一个非数字的属性,你怎么拿来运算?
	public static boolean isDigit(String str) {
		boolean isDigitFlag = false;

		if (str.matches("(^[-|+]?)\\d+")) {
			isDigitFlag = true;
		}

		return isDigitFlag;
	}


4. 我们经常会拼凑,传参等方式来定义文件名,所以在这种情况下,有必要对文件名合法性与否作出正则判断。
	public static boolean isInvalidFileName(String fileName) {
		boolean isInvalidFileNameFlag = false;

		if (fileName.matches(".*[\\/:\\*\\?\"<>\\|].*")) {
			isInvalidFileNameFlag = true;
		}

		return isInvalidFileNameFlag;
	}


5. 我们对特殊字符的处理,也要严谨,考虑方方面面,比如:在文件中将'&'转换成'&amp;','<'转换成'&lt;','>'转换成'&gt;',在这种情况下,你就不能将'&amp;'和'&lt;',以及'&gt;'中的'&'再转换成'&amp;'了。
	public static String replaceReserveSymbel(String convertPartStr) {
		if (convertPartStr != null) {
			if (convertPartStr.contains("&")) {
				StringBuffer sb = new StringBuffer("");
				convertPartStr = replaceReserveSymbel(convertPartStr, sb);
			}

			convertPartStr = convertPartStr.replace("<", "&lt;");
			convertPartStr = convertPartStr.replace(">", "&gt;");

			convertPartStr = convertPartStr.replace("\\", "\\\\");
			convertPartStr = convertPartStr.replace("{", "\\{");
			convertPartStr = convertPartStr.replace("}", "\\}");
			convertPartStr = convertPartStr.replace("(", "\\(");
			convertPartStr = convertPartStr.replace(")", "\\)");
			convertPartStr = convertPartStr.replace(",", "\\,");
		}

		return convertPartStr;
	}

	public static String replaceReserveSymbel(String convertPartStr, StringBuffer sb) {
		int index = convertPartStr.indexOf("&");

		String leftStr = "";
		String rightStr = "";

		if (index >= 0) {
			leftStr = convertPartStr.substring(0, index + 1);
			rightStr = convertPartStr.substring(index + 1);

			sb.append(leftStr);

			if (!rightStr.startsWith("amp;") && !rightStr.startsWith("lt;") && !rightStr.startsWith("gt;")) {
				sb.append("amp;");
			}

			replaceReserveSymbel(rightStr, sb);
		} else {
			sb.append(convertPartStr);
		}

		return sb.toString();
	}


6. 在多线程编程中,我们很多时候要实现接口Callable<?>, 而非实现接口Runnable,接口Callable<?>给我们带来的方便是不言而喻的,因为可以重写它的方法call()获得我们想要的返回值。
/**
 * @author shengshu
 * 
 */
public class ConverterTask implements Callable<File> {
	private static final Logger LOG = Logger.getLogger(ConverterTask.class);

	private File templateFile = null;
	private File fragmentFile = null;
	private String exportDirectoryStr = null;
	private String notificationDirectoryStr = null;

	public String fileNamePrefix = null;

	private static final Date date = new Date();

	public ConverterTask(File fragmentFile, File templateFile, String destDirectoryStr, String notificationDirectoryStr, String fileNamePrefix) {
		this.fragmentFile = fragmentFile;
		this.templateFile = templateFile;
		this.exportDirectoryStr = destDirectoryStr;
		this.notificationDirectoryStr = notificationDirectoryStr;

		this.fileNamePrefix = fileNamePrefix;
	}

	public File convertFile(File fragmentFile, File templateFile, String exportDirectoryStr, String notificationDirectoryStr, String fileNamePrefix) {

		......

		return exportFile;
	}

	public void convertFile(File fragmentFile, File templateFile, File exportDirectory, File notificationDirectory, String fileNamePrefix) throws Throwable {
		convertFile(fragmentFile, templateFile, exportDirectory.getAbsolutePath(), notificationDirectory.getAbsolutePath(), fileNamePrefix);
	}

	@Override
	public File call() throws Exception {
		File exportFile = convertFile(fragmentFile, templateFile, exportDirectoryStr, notificationDirectoryStr, fileNamePrefix);

		return exportFile;
	}

}


	private static void initConverter() {
		LOG.info("Converter thread pool size: " + threadPoolSize);
		converterThreadPool = Executors.newFixedThreadPool(threadPoolSize);

		converterFutureList = new ArrayList<Future<File>>();

		if (sourceDirectory != null && sourceDirectory.isDirectory()) {
			fragmentFileCollection = FileUtils.listFiles(sourceDirectory, new String[] { "xml", "XML" }, false);
		} else {
			LOG.warn("The directory " + sourceDirectoryStr + " doesn't exist or is invalid!");
		}

		if (templateDirectory != null && templateDirectory.isDirectory()) {
			templateFileCollection = FileUtils.listFiles(templateDirectory, new String[] { "xsl", "XSL" }, false);
		} else {
			LOG.warn("The directory " + templateDirectoryStr + " doesn't exist or is invalid!");
		}
	}

	public void converter() {
		LOG.info("==================Converter Start==================");
		startTime = System.currentTimeMillis();

		initConverter();

		if ((fragmentFileCollection != null && !fragmentFileCollection.isEmpty()) && (templateFileCollection != null && !templateFileCollection.isEmpty())) {

			Iterator<File> fragmentFileIterator = fragmentFileCollection.iterator();

			while (fragmentFileIterator.hasNext()) {
				File fragmentFile = fragmentFileIterator.next();
				LOG.info("Fragment file: " + fragmentFile);

				Iterator<File> templateFileIterator = templateFileCollection.iterator();

				while (templateFileIterator.hasNext()) {
					File templateFile = templateFileIterator.next();
					LOG.info("Template file: " + templateFile);

					ConverterTask converterTask = new ConverterTask(fragmentFile, templateFile, exportDirectoryStr, notificationDirectoryStr, fileNamePrefix);

					Future<File> converterFuture = converterThreadPool.submit(converterTask);

					converterFutureList.add(converterFuture);
				}
			}
		}

		destroyConverter();

		endTime = System.currentTimeMillis();
		deltaTime = endTime - startTime;
		LOG.info("Converter spend " + deltaTime / 1000 + " seconds");
		LOG.info("==================Converter End==================");
	}

	private static void destroyConverter() {
		LOG.info("===Start to destroy thread pool and delete files under source directory===");

		for (Future<File> future : converterFutureList) {
			try {
				File exportFile = future.get();

				if (exportFile.exists()) {
					LOG.info("Invoke method call() and return : " + exportFile);
					exportFileCollection.add(exportFile);
				}
			} catch (InterruptedException ie) {
				ie.printStackTrace();
			} catch (ExecutionException ee) {
				ee.printStackTrace();
			}
		}

		converterThreadPool.shutdown();
		LOG.info("Thread pool is changed as status: SHUTDOWN");

		while (!converterThreadPool.isTerminated())
			;

		LOG.info("Thread pool is changed as status: STOP");

		if (!CommonTool.deleteFileCollection(fragmentFileCollection)) {
			LOG.error("Fail to delete fragment files under directory " + sourceDirectoryStr);
		}

		LOG.info("===End to destroy thread pool and delete files under source directory===");
	}


7. 我们会经常处理多线程,高并发的问题,往往会用到线程池,所以我们既要保证任务都能被执行,又能保证线程池正常终止。
		subSpliterThreadPool.shutdown();

		while (!subSpliterThreadPool.isTerminated())
			;


8. 在处理XML文件时,对XSLT的应用将会大大简化我们的代码逻辑与数量,实现代码与文本处理的解耦。
<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:ne="http://www.3gpp.org/ftp/specs/archive/32_series/32.615#configData"
	xmlns:xn="http://www.3gpp.org/ftp/specs/archive/32_series/32.625#genericNrm"
	xmlns:en="http://www.3gpp.org/ftp/specs/archive/32_series/32.765#eutranNrm">
	<xsl:output method="xml" indent="yes" encoding="UTF-8" />

	<xsl:template match="/">
		<NrmFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			xsi:noNamespaceSchemaLocation="NrmFileFormat.xsd">
			<xsl:element name="FileHeader">
				<InfoModelReferenced>CMCC-ENB-NRM-V2.1.0</InfoModelReferenced>
				<DnPrefix>
					<xsl:value-of select="/ne:bulkCmConfigDataFile/ne:configData/@dnPrefix" />
				</DnPrefix>

				<xsl:variable name="SystemDN"
					select="/ne:bulkCmConfigDataFile/ne:configData/xn:SubNetwork/xn:ManagementNode/xn:IRPAgent/xn:attributes/xn:SystemDN" />

				<SenderName>
					<xsl:value-of select="$SystemDN" />
				</SenderName>

				<VendorName>
					<xsl:value-of select="/ne:bulkCmConfigDataFile/ne:fileHeader/@vendorName" />
				</VendorName>
				<DateTime>
					<xsl:value-of select="/ne:bulkCmConfigDataFile/ne:fileFooter/@dateTime" />
				</DateTime>
			</xsl:element>
			<xsl:element name="Objects">
				<ObjectType>EnbFunction</ObjectType>
				<FieldName>
					<N i="1">EnbId</N>
					<N i="2">UserLabel</N>
					<N i="3">AdministrativeState</N>
					<N i="4">OperationalState</N>
					<N i="5">EnbId</N>
					<N i="6">X2IpAddressList</N>
					<N i="7">X2BlackList</N>
					<N i="8">X2WhiteList</N>
					<N i="9">X2HoBlackList</N>
					<N i="10">IntegrityProtAlgorithm</N>
					<N i="11">CipheringAlgorithm</N>
				</FieldName>
				<FieldValue>
					<xsl:for-each select="//xn:ManagedElement [*]">
						<xsl:variable name="ManagedBy" select="xn:attributes/xn:ManagedBy" />
						<xsl:variable name="ManagedElementID" select="@id" />

						<xsl:variable name="PrefixDN">
							<xsl:call-template name="substring-first">
								<xsl:with-param name="text" select="$ManagedBy" />
								<xsl:with-param name="splitor1" select="','" />
								<xsl:with-param name="splitor2" select="'\,'" />
							</xsl:call-template>
						</xsl:variable>

						<xsl:for-each select="en:EnbFunction[*]">
							<xsl:variable name="EnbFunctionID" select="@id" />

							<xsl:element name="Cm">
								<xsl:attribute name="Dn">
									<xsl:value-of
									select="concat($PrefixDN, ',ManagedElement=',$ManagedElementID,',EnbFunction=',$EnbFunctionID)" />
								</xsl:attribute>
								<xsl:attribute name="UserLabel">
									<xsl:value-of select="en:attributes/en:UserLabel" />
								</xsl:attribute>

								<V i="1">
									<xsl:value-of select="$EnbFunctionID" />
								</V>
								<V i="2">
									<xsl:value-of select="en:attributes/en:UserLabel" />
								</V>
								<V i="3">
									<xsl:variable name="AdministrativeState">
										<xsl:call-template name="translate-lowercase-uppercase">
											<xsl:with-param name="text"
												select="en:attributes/en:AdministrativeState" />
										</xsl:call-template>
									</xsl:variable>

									<xsl:value-of select="$AdministrativeState" />
								</V>
								<V i="4">
									<xsl:variable name="OperationalState">
										<xsl:call-template name="translate-lowercase-uppercase">
											<xsl:with-param name="text"
												select="en:attributes/en:OperationalState" />
										</xsl:call-template>
									</xsl:variable>

									<xsl:value-of select="$OperationalState" />
								</V>
								<V i="5">
									<xsl:value-of select="en:attributes/en:EnbId" />
								</V>
								<V i="6">
									<xsl:value-of select="'{'" />
									<xsl:value-of select="en:attributes/en:X2IpAddressList" />
									<xsl:value-of select="'}'" />
								</V>
								<V i="7">
									<xsl:value-of select="'{'" />
									<xsl:for-each
										select="en:attributes/en:X2BlackList/*[starts-with(name(), 'en:EnbId')][normalize-space()]">
										<xsl:value-of select="node()" />
										<xsl:if test="position() &lt; last()">
											<xsl:value-of select="','" />
										</xsl:if>
									</xsl:for-each>
									<xsl:value-of select="'}'" />
								</V>
								<V i="8">
									<xsl:value-of select="'{'" />
									<xsl:for-each
										select="en:attributes/en:X2WhiteList/*[starts-with(name(), 'en:EnbId')][normalize-space()]">
										<xsl:value-of select="node()" />
										<xsl:if test="position() &lt; last()">
											<xsl:value-of select="','" />
										</xsl:if>
									</xsl:for-each>
									<xsl:value-of select="'}'" />
								</V>
								<V i="9">
									<xsl:value-of select="'{'" />
									<xsl:for-each
										select="en:attributes/en:X2HoBlackList/*[starts-with(name(), 'en:EnbId')][normalize-space()]">
										<xsl:value-of select="node()" />
										<xsl:if test="position() &lt; last()">
											<xsl:value-of select="','" />
										</xsl:if>
									</xsl:for-each>
									<xsl:value-of select="'}'" />
								</V>
								<V i="10">
									<xsl:value-of select="'{'" />
									<xsl:for-each
										select="en:attributes/en:IntegrityProtAlgorithm/*[starts-with(name(),'en:stringItem')][normalize-space()]">
										<xsl:value-of select="node()" />
										<xsl:if test="position() &lt; last()">
											<xsl:value-of select="','" />
										</xsl:if>
									</xsl:for-each>
									<xsl:value-of select="'}'" />
								</V>
								<V i="11">
									<xsl:value-of select="'{'" />
									<xsl:for-each
										select="en:attributes/en:CipheringAlgorithm/*[starts-with(name(),'en:stringItem')][normalize-space()]">
										<xsl:value-of select="node()" />
										<xsl:if test="position() &lt; last()">
											<xsl:value-of select="','" />
										</xsl:if>
									</xsl:for-each>
									<xsl:value-of select="'}'" />
								</V>
							</xsl:element>

						</xsl:for-each>
					</xsl:for-each>
				</FieldValue>
			</xsl:element>
		</NrmFile>
	</xsl:template>

	<xsl:template name="string-replace-all">
		<xsl:param name="text" />
		<xsl:param name="old" />
		<xsl:param name="new" />
		<xsl:choose>
			<xsl:when test="contains($text, $old)">
				<xsl:value-of select="substring-before($text,$old)" />
				<xsl:value-of select="$new" />
				<xsl:call-template name="string-replace-all">
					<xsl:with-param name="text" select="substring-after($text,$old)" />
					<xsl:with-param name="old" select="$old" />
					<xsl:with-param name="new" select="$new" />
				</xsl:call-template>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="$text" />
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template name="substring-first">
		<xsl:param name="text" />
		<xsl:param name="splitor1" />
		<xsl:param name="splitor2" />
		<xsl:choose>
			<xsl:when test="contains($text, $splitor2)">
				<xsl:value-of select="substring-before($text,$splitor2)" />
			</xsl:when>
			<xsl:otherwise>
				<xsl:choose>
					<xsl:when test="contains($text, $splitor1)">
						<xsl:value-of select="substring-before($text,$splitor1)" />
					</xsl:when>
					<xsl:otherwise>
						<xsl:value-of select="$text" />
					</xsl:otherwise>
				</xsl:choose>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template name="translate-lowercase-uppercase">
		<xsl:param name="text" />
		<xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'" />
		<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
		<xsl:value-of select="translate($text, $lowercase, $uppercase)" />
	</xsl:template>
</xsl:stylesheet>


待续...

   
发表评论
用户名: 匿名