解惑

解己之惑,解人之惑

标签:Web Service

导入的Reference不能编译

在工程中导入一个Web Service,发现竟然有编译错误,其他的工程没有问题,后来发现是因为工程的Namespace和工程的Service同名,也就是说,工程可能叫MyService,而创建的那个Web Service也叫MyService,所以创建出来的默认的namespace也是MyService,而VS创建的Web Service的stub会引用原来的namespace,所以就会导致引用MyService这个名字,但是编译器不知道你要找这个namespace还是类。因为处于同一个namespace里面的时候可以只用短类名就行了,但是生成的stub是使用全限定名的。

因为这个鄙视一下微软,因为这个是它推荐的命名规范导致的,而Java,package的名字推荐全部使用小写,而类名单词首字母大写,这样就不会出现类似的问题了。

单元测试以Windows集成方式认证的Web Service

如果直接引用Web Service写单元测试,运行以Windows集成方式认证并且不允许匿名访问的Web Service,那么运行的时候会报错,因为权限不够,可能是因为Visual Studio在运行的时候是以匿名用户的身份运行的,而非OS的用户身份,这样的情况需要写些代码来使用OS的用户身份:
        [TestInitialize]
        public void SetUp()
        {
            var basicHttpBinding = new BasicHttpBinding();

            basicHttpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;

            basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;

            var endpoint = new EndpointAddress("http://localhost/IntegrationService/IntegrationService.asmx");

            _client = new IntegrationServiceSoapClient(basicHttpBinding, endpoint);
            _client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

            _client.ChannelFactory.Credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
        }
代码中的EndpointAddress和IntegrationServiceSoapClient要换成你自己的Web Service的对应内容。

更新:
如果按照 Windows平台应用的运行身份的更新中所说的指定用户,那么这里的单元测试就不需要这样麻烦了,直接new一个Client就可以了。

Web Service的方法有很多限制

这个是这段时间发现的,Web Service的方法限制很多,简单的列举下这段时间遇到的:

  • 输入和输出参数不能用Collection等接口,如果用List最好直接用ArrayList,而且ArrayList也不能作为输入参数,只能作为返回值类型
  • 输入参数不能用Class类型
  • ArrayList和Class都可以wrapper以后用,也就是输入参数里面的类型里面包含ArrayList或Class是没有问题的

最后发点感慨,JAXB的默认实现要求比较多,可能是因为JDK的很多类那个时候是没有考虑到这些。

Class has two properties of the same name

开发Web Service并不是很难,但是没有好的例子,网上的一些例子都过于简单,做一个DEMO样的可以跑起来的Web Service确实不难,但是要开发一个真正实用的还是需要花些功夫的。
这个是最近遇到的一个问题,原因就是那些需要Model的Annotation使用不当,在类上面使用了XmlAccessType,然后在getter上也使用了XmlElement,在使用stub的方式作为客户端的时候没有问题,但是使用JAX-WS的方式调用的时候就出现标题中的错误。
这个问题解决后,调用没有问题,也能返回一个对象,但是对象的所有属性全部为null,需要继续解决。
到目前位置,已经使用了下面的Client方式,或多或少存在问题,只有使用stub的方式最好(根据wsdl生成代码)

  • JaxWsProxyFactoryBean,调用成功,但是返回对象为空,和直接的JAX-WS一样
  • ClientProxyFactoryBean,调用失败,Service的方法的名称加了Annotation定义,但是从生成的SOAP信息看,是使用的arg0,arg1这样的形式,所以到Server端以后参数都为null,调用失败
  • 直接JAX-WS(javax.xml.ws.Service),和第一个一样

CXF2.0.8+Spring+Hibernate

丢在这里备忘,也给大家一个参照,费了一些劲才配好的。
阅读全文

CXF2.1.1有问题

开始搞Web Service,用了CXF,下载了2.0.8和2.1.1,开始弄的是2.1.1,发现有问题:

java.lang.NoSuchMethodError: org.jaxen.BaseXPath.<init>(Ljava/lang/String;Lorg/jaxen/Navigator;)V

怎么也发布不成功,后来修改成2.0.8,发现可以了,通过浏览器访问什么的都没有问题,然后从wsdl生成stubs后,运行unit test有问题:
java.lang.IncompatibleClassChangeError
    at org.apache.cxf.wsdl11.WSDLServiceBuilder.copyExtensionAttributes(WSDLServiceBuilder.java:120)
    at org.apache.cxf.wsdl11.WSDLServiceBuilder.buildServices(WSDLServiceBuilder.java:228)
    at org.apache.cxf.wsdl11.WSDLServiceBuilder.buildServices(WSDLServiceBuilder.java:153)
    at org.apache.cxf.wsdl11.WSDLServiceFactory.create(WSDLServiceFactory.java:117)
    at org.apache.cxf.jaxws.ServiceImpl.initializePorts(ServiceImpl.java:138)
    at org.apache.cxf.jaxws.ServiceImpl.<init>(ServiceImpl.java:129)
    at org.apache.cxf.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:55)
    at javax.xml.ws.Service.<init>(Service.java:57)
 
唉,步履维艰啊

2008年8月21日更新:
发现这些都是Classpath搞的,因为原来的工程中也包含了老版本的Web  Service的包,我们这个工程在原来的工程里面,但是我发布的时候没有包含原来的jar,但是Eclipse工程里面是依赖的,所以Server运行没有问题,在Eclipse跑单元测试的时候有问题。

2.1.1的问题是DOM4J.JAR搞的,里面也包含了jaxen的类,把那些类从jar包里面去掉问题解决

开始研究Web Service

新的项目其实是转向Web Service的,公司的整体架构将采用SOA,本来这一阶段只是个临时阶段,但是Web Service的任务要提前开始了,因为另外一个新的项目需要使用我们的Service,而现在的这个项目是基于一个老的项目,只使用EJB2,所以在原来的项目的EJB3和Web Service可以根据Annotation自动发布的基础设施都不存在了,而且架构师打算弃用JBoss,转向Tomcat,所以现在最简单的方案就是基于Axis自己写必须的东西了,还不是很清楚,要慢慢研究了。

增加一个方法要修改多少个地方?

被调到新的项目组,理论上算个Team Leader,没有真正的转正,老大最开始跟我说这边的事情我可以做主,但是美国那边有个架构师开始搭了个架子,意思是在这个架子的基础上做,试了下,发现很繁琐,为了增加一个方法,得修改好多个类。这个阶段其实是个过渡阶段,以后我们会使用Web Service,所以我们哟定义Web Service的接口,然后因为是过渡阶段,所以提供了EJB2的过渡方案和原来的系统整合,所以还得改Remote接口,Local接口可以直接从Service接口extends一下就行了,然后得修改Service的实现类,加上原先的架子里面,设计的是Service里面的每个public方法对应一个Operation类,这些类设计成Command模式,方便以后可以更好的进行负载的Dispatch,就这样,给Service增加一个接口,要完全实现得至少修改3个类,增加一个类,而且前两天也提过这个项目使用的Annotation的一个好处,但是这样一来,我要实现那个接口,也得在Finder里面增加方法。最终,我得修改4个类,增加一个类来增加一个Service接口。天啊,这个是我最讨厌的。

下周打算修改这个架子,取消EJB2的临时方案,使用Dynamic Proxy实现Service,同时可以引人Transaction(这个方案原来也用了,但是架构师以Transaction的问题否决了,那个时候还没有想到怎么解决Transaction的问题,前天突然想起来我的EJB单元测试框架就是用的自定义Transaction的),同时也抛弃Finder的那个方案,因为其实我的BaseOperation已经提供了进行Hibernate的查询的方便的基础功能。最重要的是Finder中定义的Query如果有问题的话修改后不能Hot Deploy,如果在Operation的方法体里面的修改就可以Hot Deploy。这样修改后,增加一个接口只需要修改Service然后增加一个Operation类实现就行了,只要改两个地方。

© 2019 解惑

本主题由Anders Noren提供向上 ↑