Статьи

Конечные точки и провайдеры Java SE

Недавно у меня повторилась проблема, которую я не смог решить в первый раз, когда столкнулся с ней. Когда это произошло впервые, я с радостью обслуживал контент из веб-службы на основе Java SE (если вы не знакомы с этим, это включает в себя специальное аннотирование класса и его экспонирование путем публикации его в javax.xml.ws.Endpoint ). Я столкнулся с проблемой, когда попытался получить доступ к веб-службе (я полагаю, из приложения Flex), потому что среда выполнения Flex действительно «хотела» найти файл crossdomain.xml в корне HTTP-сервера. Поскольку он не мог этого сделать, он принудительно отказался подвергать мой веб-сервис возможной атаке и не получал доступ к веб-сервису.

Оставив в стороне философию обеспечения безопасности клиента (это не защищает мой веб-сервис от других недобросовестных клиентов), у меня осталась проблема. Этот подход немного наивен, потому что клиент предполагает, что у веб-службы есть что-то вроде корневого каталога документов, где вы можете просто удалить файл crossdomain.xml и продолжить свою жизнь. Как вы знаете, когда вы представляете Java-бин в качестве конечной точки, файловая система отсутствует; просто внутренний HTTP-сервер, обслуживающий запросы веб-службы. Так куда вы положили файл?

Ответ таков: где вы хотите, до тех пор, пока вы не поймете, как это подать! Недавно я столкнулся с этой же проблемой, когда создавал приложение на основе Silverlight. Silverlight налагает на себя те же самые ограничения и действительно будет искать файл crossdomain.xml (после поиска сначала файла clientaccesspolicy.xml ). На этот раз я работал немного усерднее, чтобы найти ответ, и мне посчастливилось отдохнуть от публикации в Интернете.

Используя tcpmon , я заметил интересный момент. Хотя моя веб-служба размещалась по URL-адресу, например http://www.example.com:8080/WebManager , среда выполнения Silverlight фактически ищет файл политики по адресу http://www.example.com:8080/crossdomain.xml., Другими словами, не в контексте, в котором был развернут мой веб-сервис, а в корне самого HTTP-сервера. Это различие значительно облегчает решение.

Решение состоит в том, чтобы опубликовать две конечные точки — одну для веб-службы и одну для сервера документов. Класс javax.xml.ws.Endpoint позволяет публиковать несколько bean-компонентов на одном хосте и порте (критическая точка, которая заставляет это решение работать). Вам просто нужно убедиться, что контекст или полный URL-адрес отличается для каждого. Итак, пока я могу опубликовать свой веб-сервис со следующим утверждением:

Endpoint.publish("http://www.example.com:8080/WebManager", new WebManager());

Я могу опубликовать другую конечную точку для удовлетворения Flex / Silverlight с помощью:

Endpoint.publish("http://www.example.com:8080/", new DocumentProvider());

Когда мой элемент управления Silverlight пытается получить доступ к веб-службе, он сначала ищет файл crossdomain.xml в корне веб-сервера; если он определит, что ему разрешен доступ к службе, он сделает это.

Что такое DocumentProvider ? Это класс, который я опубликовал для конечной точки во многом как моя настоящая бизнес-логика, но он перехватывает запросы HTTP и обрабатывает только запросы на документы. Другими словами, вместо того, чтобы позволить реализации Java SE обрабатывать все запросы, делегируя их методам, аннотированным @WebMethod, она обрабатывает всю работу. API для этого можно найти по адресу javax.xml.ws.Provider , который определяет один метод:

public T invoke(T request);

где T должен быть либо источник , SOAPSource или DataSource . Это выглядит немного общим, и это так. Но здесь происходит вся работа. В моем случае, я собираюсь вернуть DataSource чей входной поток запрашиваемый документ.

Ниже приводится полный класс, который реализует только сервер документов (представление методов в качестве операций веб-службы довольно хорошо задокументировано):

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import javax.activation.DataSource;
import javax.annotation.Resource;
import javax.xml.ws.BindingType;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;

@WebServiceProvider
@BindingType(HTTPBinding.HTTP_BINDING)
@ServiceMode(value=Service.Mode.MESSAGE)
public class DocumentServer implements Provider
{

protected static final String CROSSDOMAIN_XML = "<?xml version=\"1.0\" ?><cross-domain-policy><allow-access-from domain=\"*\" /></cross-domain-policy>";

@Resource
protected WebServiceContext wsContext;

public static void main(String [] args)
{
DocumentServer docServer = new DocumentServer();
}

public DocumentServer()
{
Endpoint.publish("http://localhost:10486/", this);
}

public DataSource invoke(DataSource ds) throws WebServiceException
{
MessageContext msgCtx = wsContext.getMessageContext();

String method = (String)msgCtx .get(MessageContext.HTTP_REQUEST_METHOD);
String pathInfo = (String)msgCtx.get(MessageContext.PATH_INFO);
if (method.equals("GET") && pathInfo.equals("crossdomain.xml"))
{
return new DataSource()
{
public InputStream getInputStream()
{
return new ByteArrayInputStream(CROSSDOMAIN_XML.getBytes());
}
public OutputStream getOutputStream()
{
return null;
}
public String getContentType()
{
return "text/xml";
}
public String getName()
{
return "";
}
};
}

throw new WebServiceException();
}
}

Некоторые замечания по поводу приведенного выше кода:

  • В интерфейсе провайдера много гибкости .
  • То, как ваша реализация Provider интерпретируется компилятором Java, во многом зависит от аннотаций, которые вы добавили в свою реализацию. В WebServiceProvider , BindingType и ServiceMode аннотации тесно связаны со спецификой вашей реализации. Попробуйте составить пример без них, чтобы понять, что я имею в виду.
  • WebContext имеет жизненно важное значение , но может быть получен простой инъекцией.
  • Я только обрабатываю запросы «GET» для « crosssdomain.xml » и возвращаю очень либеральную политику; конечно, это всего лишь пример. Вы также можете легко создать веб-интерфейс управления в своей междоменной политике и создавать его динамически, а также обслуживать его и другие документы из файловой системы, а не так, как я это делал здесь.

Наконец, я не эксперт в этой области, но с удовольствием передаю информацию, так как найти примеры реализации провайдера довольно сложно
. В этом духе я благодарю людей, которые разместили этот пример:
http://acm2010.cct.lsu.edu/localdoc/java/6-jdk/sample/webservices/EbayClient/ . Это было высоко оценено!

От http://wayne-adams.blogspot.com/2010/12/java-se-endpoints-and-providers.html