Недавно у меня была возможность поработать над созданием платформы 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>
Отлично!