首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >cxf-codegen-plugin不符合cxf ValidationFeature。

cxf-codegen-plugin不符合cxf ValidationFeature。
EN

Stack Overflow用户
提问于 2017-01-25 12:57:01
回答 2查看 4.2K关注 0票数 1

根据ValidationFeature文档,为了进行验证,必须用@Valid注释输入和输出绑定

但是,由cxf-codegen-plugin生成的webservice接口没有这些注释,而且我似乎找不到允许添加它们的命令行参数或插件。

在不违反Liskov替换原则的情况下,不能将@Valid注释放在webservice接口的实现中:在本例中,JSR-349 (Hibernate Validator)的引用实现生成HV000151:重写另一种方法的方法不能更改参数约束配置。

问题:有人知道用@Valid注释cxf生成的webservice接口方法参数的方法吗?

我知道Annox插件的存在,但这似乎不是一项容易完成的任务。最简单的解决方案可能是手动将@Valid注释添加到webservice接口,但我不喜欢修改生成的代码

示例

pom.xml

代码语言:javascript
复制
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example.www</groupId>
    <artifactId>webservice-test-bval</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>Test bean validation on web service</name>

    <properties>
        <org.springframework.boot.version>1.4.2.RELEASE</org.springframework.boot.version>
        <com.github.krasa.krasa-jaxb-tools>1.5</com.github.krasa.krasa-jaxb-tools>
        <org.apache.cxf.version>3.1.3</org.apache.cxf.version>
        <cxf-codegen-plugin.version>3.0.1</cxf-codegen-plugin.version>
    </properties>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- Import dependency management from Spring Boot -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${org.springframework.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <!-- Spring boot dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!-- CXF dependencies -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
        <!-- Schema validation -->
        <dependency>
            <groupId>com.github.krasa</groupId>
            <artifactId>krasa-jaxb-tools</artifactId>
            <version>${com.github.krasa.krasa-jaxb-tools}</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.validation</groupId>
                    <artifactId>validation-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${org.springframework.boot.version}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>${cxf-codegen-plugin.version}</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceRoot>${project.build.directory}/generated/</sourceRoot>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>${project.basedir}/src/main/resources/wsdl/test.wsdl</wsdl>
                                    <wsdlLocation>classpath:wsdl/test.wsdl</wsdlLocation>
                                    <extraargs>
                                        <extraarg>-xjc-XJsr303Annotations</extraarg>
                                    </extraargs>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>com.github.krasa</groupId>
                        <artifactId>krasa-jaxb-tools</artifactId>
                        <version>${com.github.krasa.krasa-jaxb-tools}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

src/main/resources/wsdl/

test.wsdl

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.example.org/test/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    name="test" targetNamespace="http://www.example.org/test/">
    <wsdl:types>
        <xsd:schema targetNamespace="http://www.example.org/test/">
            <xsd:element name="NewOperation">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="in">
                            <xsd:simpleType>
                                <xsd:restriction base="xsd:int">
                                    <xsd:minInclusive value="10" />
                                    <xsd:maxInclusive value="20" />
                                </xsd:restriction>
                            </xsd:simpleType>
                        </xsd:element>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="NewOperationResponse">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="out">
                            <xsd:simpleType>
                                <xsd:restriction base="xsd:string">
                                    <xsd:pattern value="[A-Z]+" />
                                </xsd:restriction>
                            </xsd:simpleType>
                        </xsd:element>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="NewOperationRequest">
        <wsdl:part element="tns:NewOperation" name="parameters" />
    </wsdl:message>
    <wsdl:message name="NewOperationResponse">
        <wsdl:part element="tns:NewOperationResponse" name="parameters" />
    </wsdl:message>
    <wsdl:portType name="testWS">
        <jaxws:bindings xmlns:jaxws="http://java.sun.com/xml/ns/jaxws">
            <jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
        </jaxws:bindings>
        <wsdl:operation name="NewOperation">
            <wsdl:input message="tns:NewOperationRequest" />
            <wsdl:output message="tns:NewOperationResponse" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="testWSSOAP" type="tns:testWS">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="NewOperation">
            <soap:operation soapAction="http://www.example.org/test/NewOperation" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="testWS">
        <wsdl:port binding="tns:testWSSOAP" name="testWSSOAP">
            <soap:address location="http://www.example.org/" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

src/main/java/

org.example.test

代码语言:javascript
复制
package org.example.test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

package org.example.test;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.validation.Valid;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;

import org.example.test.ObjectFactory;

@WebService(targetNamespace = "http://www.example.org/test/", name = "testWS")
@XmlSeeAlso({ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface TestWSValid {

    @WebMethod(operationName = "NewOperation", action = "http://www.example.org/test/NewOperation")
    @Valid @WebResult(name = "NewOperationResponse", targetNamespace = "http://www.example.org/test/", partName = "parameters")
    public NewOperationResponse newOperation(
            @Valid @WebParam(partName = "parameters", name = "NewOperation", targetNamespace = "http://www.example.org/test/")
        NewOperation parameters
    );
}

org.example.test.configuration

代码语言:javascript
复制
package org.example.test.configuration;

import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.feature.Feature;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.apache.cxf.validation.BeanValidationFeature;
import org.example.test.services.TestWSImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource({ "classpath:META-INF/cxf/cxf.xml", "classpath:META-INF/cxf/cxf-servlet.xml" })
@ComponentScan({ "org.example.test" })
public class ApplicationConfiguration {

    @Autowired
    private Bus cxfBus;

    @Bean
    public Endpoint testWSEndpoint() {
        EndpointImpl endpoint = new EndpointImpl(cxfBus, new TestWSImpl());
        endpoint.setAddress("/testws");
        endpoint.publish();
        return endpoint;
    }

    @Bean
    public ServletRegistrationBean cxfServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new CXFServlet(), "/services/*");
        servlet.setLoadOnStartup(1);
        return servlet;
    }

    @Bean
    public Feature validationFeature() {
        Feature validationFeature = new BeanValidationFeature();
        validationFeature.initialize(cxfBus);
        cxfBus.getFeatures().add(validationFeature);
        ConstraintViolationInterceptor interceptor = new ConstraintViolationInterceptor();
        cxfBus.getInFaultInterceptors().add(interceptor);
        cxfBus.getOutFaultInterceptors().add(interceptor);
        cxfBus.getProperties().put("exceptionMessageCauseEnabled", true);
        return validationFeature;
    }
}

package org.example.test.configuration;

import java.text.MessageFormat;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolationException;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.binding.soap.interceptor.Soap11FaultOutInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;

public class ConstraintViolationInterceptor extends AbstractSoapInterceptor {

    public ConstraintViolationInterceptor() {
        super(Phase.MARSHAL);
        getBefore().add(Soap11FaultOutInterceptor.class.getName());
    }

    private static final String TEMPLATE = "[{0}] {1} : {2}";

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        Fault fault = (Fault) message.getContent(Exception.class);
        Throwable exception = fault.getCause();
        if (exception instanceof ConstraintViolationException) {
            fault.setMessage(processConstraints((ConstraintViolationException) exception));
        }
    }

    private String processConstraints(ConstraintViolationException exception) {
        return exception.getConstraintViolations().stream().map((error) -> {
            return MessageFormat.format(TEMPLATE, error.getPropertyPath(), error.getMessage(), error.getInvalidValue());
        }).collect(Collectors.joining(System.lineSeparator()));
    }

}

org.example.test.services

代码语言:javascript
复制
package org.example.test.services;

import javax.jws.WebService;

import org.example.test.NewOperation;
import org.example.test.NewOperationResponse;
import org.example.test.ObjectFactory;
import org.example.test.TestWS;

@WebService(endpointInterface = "org.example.test.TestWS", portName = "TestWSPort", serviceName = "TestWS", targetNamespace = "http://www.example.org/test/")
public class TestWSImpl implements TestWS {

    @Override
    public NewOperationResponse newOperation(NewOperation parameters) {
        int in = parameters.getIn();
        NewOperationResponse response = new ObjectFactory().createNewOperationResponse();
        if (in < 10 || in > 20) {
            response.setOut("no no no");
        } else {
            response.setOut("OK");
        }
        return response;
    }

}

对于上面的项目,您可以测试,只要TestWSImpl implements TestWS (生成的类)没有进行验证,但是如果TestWSImpl implements TestWSValid (包含生成的代码和@Valid加法的类),那么验证就像预期的那样工作。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-02-05 11:18:04

Apache的wsdl2java支持是可插拔的。有一个META/tools-plugin.xml描述符,允许您定义自定义生成器(“前端配置文件”)。因此,如果在所有cxf生成的webservice接口上都需要一个@Valid注释,则只需插入一个自定义SEIGenerator即可。Apache使用速度模板生成SEI接口。因此,您只需用自定义模板覆盖默认模板即可。

因此,您可以创建一个简单的cxf-codegen-plugin覆盖,而不是使用Anox或Krasa。

因此,让我们创建一个单独的项目,你仍然可以把它放在同一个项目,但在不同的模块,但为了更好的可重用性,我想说一个新的项目。

pom.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <groupId>org.example.test</groupId>
    <artifactId>valid-cxf</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modelVersion>4.0.0</modelVersion>

    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-codegen-plugin</artifactId>
            <version>${org.apache.cxf.version}</version>
        </dependency>
    </dependencies>

</project>

让我们使用服务加载器来定义新的默认SEI生成器。

src/main/resources/META-INF/tools-plugin.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<plugin xmlns="http://cxf.apache.org/tools/plugin" name="play" version="" provider="play.typesafe.com">
    <frontend name="sample" package="org.apache.cxf.tools.wsdlto.frontend.jaxws" profile="JAXWSProfile">
    <container name="JAXWSContainer" package="org.apache.cxf.tools.wsdlto.frontend.jaxws" toolspec="jaxws-toolspec.xml"/>
    <processor name="WSDLToJavaProcessor" package="org.apache.cxf.tools.wsdlto.frontend.jaxws.processor"/>
    <builder name="JAXWSDefinitionBuilder" package="org.apache.cxf.tools.wsdlto.frontend.jaxws.wsdl11"/>
    <generators package="com.example.plugin">
        <generator name="CustomSEIGenerator"/>
    </generators>
</frontend>

在这里,我们从包SEIGenerator定义了一个名为CustomSEIGenerator的新com.example.plugin

接下来,让我们定义一个包含所有覆盖的速度模板(在我们的例子中,只是web服务接口上硬编码的@Valid注释),这个模板基于官方的CXF sei.vm

src/main/resources/valid-sei.vm

代码语言:javascript
复制
#if ($intf.packageJavaDoc != "")
/**
$intf.packageJavaDoc
 */
#end
package $intf.PackageName;

#if ($mark-generated == "true")
import javax.annotation.Generated;
#end
import javax.validation.Valid;
#foreach ($import in $intf.Imports)
import ${import};
#end

/**
#if ($intf.classJavaDoc != "")
 $intf.classJavaDoc
 *
 #end
 * This class was generated by $fullversion
 * $currentdate
 * Generated source version: $version
 * 
 */
#foreach ($annotation in $intf.Annotations)
 $annotation
#end
#if ($mark-generated == "true")
@Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date =     "$currentdate", comments = "$fullversion")
#end
public interface $intf.Name ${sei-superinterface-string}{
#foreach ($method in $intf.Methods)

#if ($method.JavaDoc != "")
    /**
${method.JavaDoc}
     */
#end
#foreach ($annotation in $method.Annotations)
    $annotation
#end
@Valid
#if ($mark-generated == "true")
    @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava", date = "$currentdate")
#end
    public $method.returnValue ${method.Name}(#if($method.ParameterList.size() == 0))#end
#if($method.ParameterList.size() != 0)

#foreach ($param in ${method.ParameterList})
    $param
#end
)#end#if($method.Exceptions.size() > 0) throws#foreach($exception in $method.Exceptions) $exception.ClassName#if($method.Exceptions.size() != $velocityCount),#end#end#end;
#end
}

最后让我们创建一个CustomSEIGenerator,它将使用我们的速度模板。

代码语言:javascript
复制
package org.example.test;

import org.apache.cxf.tools.common.ToolException;
import org.apache.cxf.tools.wsdlto.frontend.jaxws.generators.SEIGenerator;

import java.io.Writer;

/**
 * Just a sample custom generator which use custom velocity template to generate SEI
 *
 */
public class CustomSEIGenerator extends SEIGenerator {

    @Override
    protected void doWrite(String templateName, Writer outputs) throws ToolException {

        if (templateName.endsWith("/sei.vm")) {
            templateName = "valid-sei.vm";
        }

        super.doWrite(templateName, outputs);
    }
}

假设您已经构建了插件覆盖并发布到本地maven repo,那么接下来您只需要向项目pom.xml添加一个依赖信息。因此,问题中的pom将保持不变,只有cxf-codegen-plugin才会获得新的依赖项。

In pom.xml

代码语言:javascript
复制
<plugin>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-codegen-plugin</artifactId>
        <version>${cxf-codegen-plugin.version}</version>
        <executions>
            <execution>
                <id>generate-sources</id>
                <phase>generate-sources</phase>
                <configuration>
                        <sourceRoot>${project.build.directory}/generated/</sourceRoot>
                        <wsdlOptions>
                            <wsdlOption>
                                <wsdl>${project.basedir}/src/main/resources/wsdl/test.wsdl</wsdl>
                                <wsdlLocation>classpath:wsdl/test.wsdl</wsdlLocation>
                            </wsdlOption>
                            <extraargs>
                                <extraarg>-fe</extraarg>
                                <extraarg>sample</extraarg>
                            </extraargs>
                        </wsdlOptions>
                    </configuration>
                    <goals>
                        <goal>wsdl2java</goal>
                    </goals>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                <groupId>org.example.test</groupId>                      
                <artifactId>valid-cxf</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    </plugin>

就是这样,现在您可以完全管理从wsdl中生成的内容和方法。

在此回购https://github.com/babltiga/cxf-valid-sample中可以找到一个完全工作的示例。

票数 1
EN

Stack Overflow用户

发布于 2017-02-05 18:22:26

为了记录在案,我认为对CustomSEIGenerator实现最好的是:

代码语言:javascript
复制
package org.example.test;

import java.util.List;
import java.util.Map;

import javax.validation.Valid;
import javax.xml.namespace.QName;

import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.tools.common.ToolContext;
import org.apache.cxf.tools.common.ToolException;
import org.apache.cxf.tools.common.model.JAnnotation;
import org.apache.cxf.tools.common.model.JavaInterface;
import org.apache.cxf.tools.common.model.JavaMethod;
import org.apache.cxf.tools.common.model.JavaModel;
import org.apache.cxf.tools.common.model.JavaParameter;
import org.apache.cxf.tools.wsdlto.frontend.jaxws.generators.SEIGenerator;
import org.apache.cxf.tools.wsdlto.frontend.jaxws.processor.WSDLToJavaProcessor;

public class CustomSEIGenerator extends SEIGenerator {

  private static final String VALID_PARAM  = "VALID_PARAM";

  private static final String VALID_RETURN = "VALID_RETURN";

  @Override
  public void generate(ToolContext penv) throws ToolException {
    JAnnotation validAnno = new JAnnotation(Valid.class);
    Map<QName, JavaModel> map = CastUtils.cast((Map<?, ?>) penv.get(WSDLToJavaProcessor.MODEL_MAP));
    for (JavaModel javaModel : map.values()) {
      Map<String, JavaInterface> interfaces = javaModel.getInterfaces();

      for (JavaInterface intf : interfaces.values()) {
        intf.addImport(Valid.class.getCanonicalName());
        List<JavaMethod> methods = intf.getMethods();

        for (JavaMethod method : methods) {
          List<JavaParameter> parameters = method.getParameters();
          method.addAnnotation(VALID_RETURN, validAnno);
          for (JavaParameter param : parameters) {
            param.addAnnotation(VALID_PARAM, validAnno);
          }
        }
      }
    }

    super.generate(penv);
  }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41852213

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档