Статьи

Базовый WS с Scala, Sbt и JEE 6


Недавно у меня была возможность поработать над созданием платформы SOA с использованием JEE 6 и Scala.
Я поделюсь здесь некоторыми фрагментами, которые могут быть полезны для начала публикации веб-сервисов с использованием Scala, Sbt и JAX-WS.

Рассмотрим пример для вычисления коэффициента и остатка с использованием евклидова алгоритма. Не очень сложно, если входные данные являются положительными числами.

Я- сб

Давайте создадим проект sbt и добавим необходимые зависимости

$ mkdir sws $ cd sws $ touch build.sbt $ отредактируйте build.sbt следующим образом:


 

name := "Euclide"

version := "1.0"

organization := "me.ouertani"

scalaVersion := "2.9.1"

scalaSource in Compile <<= baseDirectory(_ / "src")

scalaSource in Test <<= baseDirectory(_ / "test")

libraryDependencies += "javax" % "javaee-api" % "6.0" % "provided" 

libraryDependencies += "log4j" % "log4j" % "1.2.16" % "provided" 

libraryDependencies += "junit" % "junit" % "4.8.2" % "test"
 

$ mkdir src
$ touch src / Euclide.scala

Это все для конфигурации проекта.

II- Создать WS

Мы будем использовать один файл Euclide.scala для всех классов, это будет легче для наглядности

1 — Создайте класс запроса и не забудьте:
a — конструктор по умолчанию
b — аннотация доступа к полю

@XmlAccessorType(XmlAccessType.FIELD)
case class Request(a : Int, b : Int ){ def this(){this(0,0)}}

2- Создайте класс ответа

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "output")
case class Response (c: Int, d: Int){ def this(){this(0,0)}}

3. Почему бы не создать класс исключений для обработки исключений, таких как b равен 0?

 
@XmlAccessorType(XmlAccessType.FIELD)
case class ResponseException(why : String){  def this(){this("")}}

@XmlAccessorType(XmlAccessType.FIELD)
case class FaultResponse(@BeanProperty faultInfo : ResponseException) extends Exception { def this(){this(ResponseException(""))}}

Не забудьте добавить аннотацию BeanProperty к входу faultInfo!

4- Давайте добавим перехватчик для регистрации всех вызовов метода ws

object TracingInterceptor{
  import org.apache.log4j.Logger
  val L = Logger.getLogger(classOf[TracingInterceptor])
}

import TracingInterceptor._
class TracingInterceptor { 

  @AroundInvoke
  def  log( context:InvocationContext):Object ={
    try {
      if (L.isDebugEnabled()) {
        val clazz = context.getMethod().getDeclaringClass().getName()
        val method = context.getMethod().getName()        
        L.debug(clazz + " : Is invoking method: " + method + " With Parameters : " + context.getParameters().mkString("[", ",","]"))
      }
    } catch { 
      case  ex :Exception =>    L.warn("Unable to Intercept Method Call", ex)
    } 
    context.proceed()
  }
}

5- Класс WS и его метод деления с использованием intercpetor и EJB 3.1 без сохранения состояния

@WebService(serviceName = "Euclide", targetNamespace = "http://slim.ouertani.me/")
@Stateless()
@Interceptors(Array(classOf[TracingInterceptor]))
class Euclide {
  @throws(classOf[FaultResponse])
  def divide( @WebParam(name="input" )  @XmlElement(required = true, nillable = false)
    req :Request):Response =  req match {
      case Request(_, 0 ) => throw FaultResponse(ResponseException("Can't divide by ZERO!"))
      case Request(a, b ) =>  Response(a / b ,a % b) 
    }
}

6- Не забудьте пакет, импортирует в начало файла, и это все:

package me.ouertani.scala.ws

import javax.interceptor.Interceptors
import javax.xml.bind.annotation.XmlElement
import javax.interceptor.{ AroundInvoke , InvocationContext }
import javax.xml.bind.annotation. { XmlAccessType , XmlAccessorType, XmlElement, XmlType}
import javax.ejb. { EJB ,Stateless }
import javax.jws. {WebService , WebParam }
import scala.reflect.BeanProperty

III- упаковка и запуск WS

1- Давайте начнем скомпилировать наш projet
$ xsbt compile
2 — упаковать пакет jar
$ xsbt
3 — развернуть его в своем контейнере JEE 6 (мой — Glassfish 3.1.1)

IV — проверить это
1- После депозита давайте проверим это с помощью инструмента soapUI

Запрос 1:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:slim="http://slim.ouertani.me/">
   <soapenv:Header/>
   <soapenv:Body>
      <slim:divide>
         <input>
            <a>6</a>
            <b>4</b>
         </input>
      </slim:divide>
   </soapenv:Body>
</soapenv:Envelope>

 

Ответ 1:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:divideResponse xmlns:ns2="http://slim.ouertani.me/">
         <return>
            <c>3</c>
            <d>0</d>
         </return>
      </ns2:divideResponse>
   </S:Body>
</S:Envelope>

Запрос 2:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:slim="http://slim.ouertani.me/">
   <soapenv:Header/>
   <soapenv:Body>
      <slim:divide>
         <input>
            <a>6</a>
            <b>0</b>
         </input>
      </slim:divide>
   </soapenv:Body>
</soapenv:Envelope>

Ответ 2:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
         <faultcode>S:Server</faultcode>
         <faultstring>me.ouertani.scala.ws.FaultResponse</faultstring>
         <detail>
            <ns2:FaultResponse xmlns:ns2="http://slim.ouertani.me/">
               <faultInfo>
                  <why>Can't divide by ZERO!</why>
               </faultInfo>
            </ns2:FaultResponse>
         </detail>
      </S:Fault>
   </S:Body>
</S:Envelope>

Отлично!