一.问题描述
在用spring-dm对某bundle进行测试时,报了如下错误。于是便对spring-osgi-test.jar中的几个测试类做了下研究。
java.io.FileNotFoundException: C:\Users\boy\.m2\repository\org\aopalliance\com.springsource.org.aopalliance\1.0.0\com.springsource.org.aopalliance-1.0.0.jar (系统找不到指定的路径。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at org.springframework.core.io.FileSystemResource.getInputStream(FileSystemResource.java:110)
at org.springframework.osgi.test.AbstractOsgiTests.installBundle(AbstractOsgiTests.java:323)
at org.springframework.osgi.test.AbstractOsgiTests.startup(AbstractOsgiTests.java:253)
at org.springframework.osgi.test.AbstractOsgiTests.prepareTestExecution(AbstractOsgiTests.java:374)
at org.springframework.osgi.test.AbstractOsgiTests.runBare(AbstractOsgiTests.java:203)
at org.springframework.osgi.test.AbstractOsgiTests$1.protect(AbstractOsgiTests.java:184)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at org.springframework.osgi.test.AbstractOsgiTests.run(AbstractOsgiTests.java:181)
二.spring dm对bundle的集成测试
在使用spring dm框架时,其中的spring-osgi-test.jar这个bundle对集成测试提供了很好的支持。其中最重要的几个类如下
(1)AbstractOsgiTests.java
(2)AbstractConfigurableOsgiTests.java
(3)AbstractDependencyManagerTests
(4)AbstractConfigurableBundleCreatorTests
1.AbstractOsgiTests类
下面说下该类中比较重要的一个方法,在这个方法中定义了bundle的加载过程。
private Resource[] locateBundles() {
//获取测试框架所需的bundle
Resource[] testFrameworkBundles = getTestFrameworkBundles();
//获取要测试的bundle.
Resource[] testBundles = getTestBundles();
if (testFrameworkBundles == null)
testFrameworkBundles = new Resource[0];
if (testBundles == null)
testBundles = new Resource[0];
Resource[] allBundles = new Resource[testFrameworkBundles.length + testBundles.length];
System.arraycopy(testFrameworkBundles, 0, allBundles, 0, testFrameworkBundles.length);
System.arraycopy(testBundles, 0, allBundles, testFrameworkBundles.length, testBundles.length);
return allBundles;
}
其中的getTestFrameworkBundles()方法和getTestBundles()方法都由子类进行实现。对于getTestFrameworkBundles()方法,需要获取到测试框架所需的bundle。而对于getTestBundles()方法需要获取到要被测试的bundle.
2.AbstractDependencyManagerTests类
AbstractDependencyManagerTests是AbstractOsgiTests的子类,在其中提供了对getTestFrameworkBundles()方法的实现。
protected Resource[] getTestFrameworkBundles() {
return locateBundles(getTestFrameworkBundlesNames());
}
在这个getTestFrameworkBundlesNames()方法中,会获取测试框架所需的bundle名称.默认情况下,会加载spring-osgi-test.jar中的boot-bundles.properties文件中指定的bundle.如果子类要更换测试框架所需的bundle,可重载AbstractDependencyManagerTests中的getTestingFrameworkBundlesConfiguration()方法。
protected Resource[] locateBundles(String[] bundles) {
if (bundles == null)
bundles = new String[0];
Resource[] res = new Resource[bundles.length];
for (int i = 0; i < bundles.length; i++) {
res[i] = locateBundle(bundles[i]);
}
return res;
}
protected Resource locateBundle(String bundleId) {
Assert.hasText(bundleId, "bundleId should not be empty");
// parse the String
String[] artifactId = StringUtils.commaDelimitedListToStringArray(bundleId);
Assert.isTrue(artifactId.length >= 3, "the CSV string " + bundleId + " contains too few values");
// TODO: add a smarter mechanism which can handle 1 or 2 values CSVs
for (int i = 0; i < artifactId.length; i++) {
artifactId[i] = StringUtils.trimWhitespace(artifactId[i]);
}
//获取本地的maven仓储。
ArtifactLocator aLocator = getLocator();
return (artifactId.length == 3 ? aLocator.locateArtifact(artifactId[0], artifactId[1], artifactId[2])
: aLocator.locateArtifact(artifactId[0], artifactId[1], artifactId[2], artifactId[3]));
}
在上面的方法中,测试框架会默认到本地的maven仓储中加载所需的bundle. 对于默认的maven仓储,如果用户没有修改过maven的setting.xml中的这项值<localRepository>/path/to/local/repo</localRepository> ,则默认路径是:
${user.home}\.m2\repository这个目录下,对于我的机器则是C:\Users\boy\.m2\repository下。由于我修改了setting.xml中localRepository为 <localRepository>c:\repository</localRepository>,系统在加载bundle时还是会去C:\Users\boy\.m2\repository中寻找,我本地的maven仓储已经不是这个目录了,当然就会出现开头说的错误信息。
三.问题解决
对于上面的错误,我们已经知道了原因,最简单的一种修改方法是将maven中的setting.xml中的localRepository配置改回默认配置。但对于我而言,个人比较喜欢比较简短的文件路径,不想修改回默认的配置,哪就只好想别的办法。
在AbstractDependencyManagerTests类中,
protected ArtifactLocator getLocator() {
return locator;
}
该方法是允许子类扩展的。Spring dm对ArtifactLocator提供的默认实现类是LocalFileSystemMavenRepository类。
该类不允许对maven中的repositoryHome进行设置。如果能有另一个实现ArtifactLocator接口的类且允许自已指定repositoryHome路径也可以灵活的解决上面的问题。下面是自定义的maven仓储定位器的实现:
public class ConfigLocalFileSystemMavenRepository implements ArtifactLocator {
/** discovered local m2 repository home */
private String repositoryHome;
public ConfigLocalFileSystemMavenRepository(String repositroyHome) {
this.repositoryHome = repositroyHome;
}
//其它方法省略,省略的方法参见 LocalFileSystemMavenRepository中的实现。
}
四.完整的测试代码如下:
public class SpringDmSampleTest extends AbstractConfigurableBundleCreatorTests {
@Override
protected String[] getTestBundlesNames() {
return new String[] { "com.mango.cache, cache.core, 1.0.0" };
}
@Override
protected Resource getTestingFrameworkBundlesConfiguration() {
return new InputStreamResource(SpringDmSampleTest.class
.getResourceAsStream("boot-bundles.properties"));
}
@Override
protected ArtifactLocator getLocator() { ArtifactLocator locator = new ConfigLocalFileSystemMavenRepository( "c:/repository"); return locator; }
}
对于要测试的bundle,一定要注意将它install到本地maven仓储中。