IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  XML | Information Management | Технология Java | Open source | SOA и Web-сервисы  >

Основы создания mashup-приложений - Web-сервисы и семантический Web: Часть 5. Смена Web-сервисов

Дайте пользователям mashup-приложений возможность выбора сервисов в вашем приложении

developerWorks
На предыдущую страницуСтраница 4 из 11 На предыдущую страницу

Опции документа

Обсудить

Исходные тексты примера


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


Получение информации о сервисе из онтологии

Теперь, когда мы знаем, с каким сервисом желает работать пользователь, необходимо извлечь информацию об этом сервисе из онтологии. Для этого необходимо добавить API к методу doPost().

Добавление информации о сервисе к онтологии

Когда мы создавали онтологию в четвертой части, мы включали базовую информацию о сервисе, а теперь нам нужны более подробные данные. В частности, нам нужно добавить дополнительную информацию о сервисе, такую как данные о выражениях записей и шаблоне, определенных в текущем классе Service, а также о способе передачи сервису параметров, например, типа поиска или идентификатора разработчика (см. листинг 13):


Листинг 13. Дополнение определения сервиса
                    
...
<owl:Class rdf:ID="Service">
   <rdfs:label>Web Service</rdfs:label>
   <rdfs:subClassOf>
     <owl:Restriction>
       <owl:onProperty rdf:resource="#endpoint" />
       <owl:minCardinality rdf:datatype=
           "&xsd;nonNegativeInteger">1</owl:minCardinality>
     </owl:Restriction>
   </rdfs:subClassOf>

   <rdfs:subClassOf>
     <owl:Restriction>
       <owl:onProperty rdf:resource="#template" />
       <owl:minCardinality rdf:datatype=
            "&xsd;nonNegativeInteger">1</owl:minCardinality>
     </owl:Restriction>
   </rdfs:subClassOf>

</owl:Class>
<owl:DatatypeProperty  rdf:ID="endpoint">
   <rdfs:domain rdf:resource="#Service"/>
   <rdfs:range rdf:resource=
                   "http://www.w3.org/2001/XMLSchema#anyURI"/>
</owl:DatatypeProperty>
<owl:ObjectProperty  rdf:ID="inputparameter">
   <rdfs:domain rdf:resource="#Service"/>
   <rdfs:range rdf:resource="#ServiceParameterMap"/>
</owl:ObjectProperty>
<owl:DatatypeProperty rdf:ID="rootoutputnode">
   <rdfs:domain rdf:resource="#Service"/>
   <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty  rdf:ID="xsltTransformationString">
   <rdfs:domain rdf:resource="#Service"/>
   <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<owl:DatatypeProperty  rdf:ID="queryParameter">
   <rdfs:domain rdf:resource="#Service"/>
   <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<owl:DatatypeProperty  rdf:ID="template">
   <rdfs:domain rdf:resource="#Service"/>
   <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty  rdf:ID="elementValue">
   <rdfs:domain rdf:resource="#Service"/>
   <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty  rdf:ID="attributeValue">
   <rdfs:domain rdf:resource="#Service"/>
   <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<owl:Class rdf:ID="ServiceParameterMap">
   <rdfs:label>Web Service Parameter Map</rdfs:label>
   <rdfs:subClassOf>
     <owl:Restriction>
       <owl:onProperty rdf:resource="#paramname" />
       <owl:cardinality 
rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
     </owl:Restriction>
   </rdfs:subClassOf>
   <rdfs:subClassOf>
     <owl:Restriction>
       <owl:onProperty rdf:resource="#paramvalue" />
       <owl:cardinality 
rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
     </owl:Restriction>
   </rdfs:subClassOf>
</owl:Class>
<owl:DatatypeProperty  rdf:ID="paramname">
   <rdfs:domain rdf:resource="#ServiceParameterMap"/>
   <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty  rdf:ID="paramvalue">
   <rdfs:domain rdf:resource="#ServiceParameterMap"/>
   <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
...

Вся информация, например, шаблон выражения записей (называемый здесь корневым узлом), очень проста. Мы можем просто добавить ее к классу в виде свойства. В некоторых случаях ситуация не будет такой простой. Например, у входных параметров есть название и значение, поэтому для хранения этой информации необходимо разработать отдельную структуру.

В этом случае структура называется ServiceParameterMap. ServiceParameterMap указывает на ресурсы paramname и paramvalue. После того как все эти свойства созданы, мы можем создать непосредственно индивидные концепты.



В начало


Отдельный сервис в рамках онтологии

Индивидные концепты сервисов создаются очень просто; достаточно следовать структуре класса (см. листинг 14):


Листинг 14. Создание индивидного концепта Bookstore
                    
<Bookstore rdf:ID="Amazon.com">
    <endpoint>http://webservices.amazon.com/onca/xml</endpoint>
    <rootoutputnode>/ItemSearchResponse/Items/Item</rootoutputnode> 
    <inputparameter rdf:resource="#amazonInput1"/>
    <inputparameter rdf:resource="#amazonInput2"/>
    <inputparameter rdf:resource="#amazonInput3"/>
    <inputparameter rdf:resource="#amazonInput4"/> 
    <inputparameter rdf:resource="#amazonInput5"/> 
    <xsltTransformationString>
       <![CDATA[
           TBD
         ]]>
    </xsltTransformationString> 
    <template>
        <![CDATA[ 
             <p><a value='href'><value/></a> by 
<value/></p> 
        ]]>
    </template>
    <elementValue>ItemAttributes/Title!ItemAttributes/Author</elementValue>
    <attributeValue>DetailPageURL</attributeValue>
    <queryParameter>Title</queryParameter>
</Bookstore>
<ServiceParameterMap rdf:ID="amazonInput1">
    <paramname>Service</paramname>
    <paramvalue>AWSECommerceService</paramvalue>
</ServiceParameterMap>
<ServiceParameterMap rdf:ID="amazonInput2">
    <paramname>AWSAccessKeyId</paramname>
    <paramvalue>000000000000000</paramvalue>
</ServiceParameterMap>
<ServiceParameterMap rdf:ID="amazonInput3">
    <paramname>SearchIndex</paramname>
    <paramvalue>Books</paramvalue>
</ServiceParameterMap>
<ServiceParameterMap rdf:ID="amazonInput5">
    <paramname>ResponseGroup</paramname>
    <paramvalue>Large</paramvalue>
</ServiceParameterMap>
<ServiceParameterMap rdf:ID="amazonInput4">
    <paramname>Operation</paramname>
    <paramvalue>ItemSearch</paramvalue>
</ServiceParameterMap>

Часть свойств, например, template и elementValue, знакомы вам по классу Service. Другие, например, ресурсы inputparameter, новые, созданные в виде отдельных экземпляров класса ServiceParameterMap, на которые впоследствии основное тело концепта сервиса ссылается как на свойство inputparameter.

Кроме того, хотя может показаться разумным включить каждое значение элемента elementValue и значение атрибута attributeValue в отдельное свойство, это вызовет проблемы в дальнейшем, так как вы не можете предсказать порядок, в котором они будут извлекаться, но вам нужно соблюсти порядок их вывода. чтобы решить эту проблему, включите все значения по порядку, разделяя их восклицательным знаком (!). Маловероятно, что восклицательный знак появится в каком-либо из выражений XPath, поэтому мы можем использовать его в качестве разделителя.

Обратите внимание, что вы можете просто добавить параметры к концу строки, но следование вышеприведенным рекомендациям даст несколько преимуществ. Во-первых, это позволит вам динамически изменять такие параметры, как, например, ключ разработчика. Во-вторых, это делает структуру более общей, тем самым облегчая адаптацию приложений как для сервисов SOAP, так и для сервисов REST.

Теперь давайте рассмотрим извлечение информации.



В начало


Извлечение индивидного концепта

Первый шаг состоит в получении ссылки на ресурс, представляющий индивидный концепт в онтологии. Поскольку значение, возвращаемое полем выбора, является URI ресурса, этот выбор осуществляется очень просто (см. листинг 15):


Листинг 15. Получение индивидного концепта
                    
...
public class MashupOntologyReader {

    private OntModel bookStoreOntModel;
        
    public MashupOntologyReader(){
       ...
    }

    public Individual[] getIndividuals(String individualType){
        ...
        return individuals;

    }
    
    public void obtainServiceInfo(String serviceURI){

        Individual bookstore = 
                   bookStoreOntModel.getIndividual(serviceURI);
        
        }
       
    }
   
    public static void main(String[] args) {

        MashupOntologyReader reader = new MashupOntologyReader();
        Individual[] individuals = reader.getIndividuals("Bookstore");
        reader.obtainServiceInfo(
                               "http://example.com/store#Amazon.com");

    }

}

Создаем новый метод obtainServiceInfo() в классе MashupOntologyGreater и с помощью метода getIndividual() получаем ссылку на нужный ресурс.

Теперь мы можем извлечь информацию о сервисе.



В начало


Получение параметров сервиса

Параметры сервиса хранятся как свойства ресурса, который представляет сервис, поэтому для получения информации нам нужно считать эти свойства (см. листинг 16):


Листинг 16. Получение параметров сервиса
                    
...
public class MashupOntologyReader {

    private OntModel bookStoreOntModel;
        
    public MashupOntologyReader(){
       ...
    }

    public Individual[] getIndividuals(String individualType){
        ...
        return individuals;

    }
    
    public void obtainServiceInfo(String serviceURI){

        Individual bookstore = 
                   bookStoreOntModel.getIndividual(serviceURI);
        
        for (StmtIterator i = bookstore.listProperties(  );
                                               i.hasNext(); ) {
            
            Statement s = i.nextStatement();
            Property p = s.getPredicate();

            if (p.canAs(OntProperty.class)){
                
                Resource r;
                Literal l;
                if (p.getLocalName().equals("endpoint")){
                    l = s.getLiteral();
                    System.out.println( p.getLocalName() + " ==> " 
                                                 + l.getString() );
                }
                else if (p.getLocalName().equals("rootoutputnode")){
                    l = s.getLiteral();
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() );
                }
                else if (p.getLocalName().equals("inputparameter")){
                    r = s.getResource();
                    System.out.println( getParameterName(r.getURI())  
                          + " ==> " + getParameterValue(r.getURI()));            
                }
                else if (p.getLocalName()
                          .equals("xsltTransformationString")){
                    l = s.getLiteral();
                    System.out.println( p.getLocalName() + " ==> " 
                                                 + l.getString() );
                }
                else if (p.getLocalName().equals("template")){
                    l = s.getLiteral();
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() );
                }
                else if (p.getLocalName().equals("elementValue")){
                    l = s.getLiteral();
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() );
                }
                else if (p.getLocalName().equals("attributeValue")){
                    l = s.getLiteral();
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() );
                }
                else if (p.getLocalName().equals("queryParameter")){
                    l = s.getLiteral();
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() );
                }
            }
        }
       
    }
   
    public String getParameterName(String uri){
        // this assumes the uri is to a ServiceParameterMap
        OntResource or = bookStoreOntModel.getOntResource(uri);
        String toReturn=null;
        for (StmtIterator i = or.listProperties(  ); i.hasNext(); ) {
            Statement s = i.nextStatement();
            Property p = s.getPredicate();
            if (p.canAs(OntProperty.class)){
                Resource r;
                Literal l;
                if (p.getLocalName().equals("paramname")){
                    l = s.getLiteral();
                    toReturn=l.getString();
                }
            }
        }               
        return toReturn;        
    }

    public String getParameterValue(String uri){
        // this assumes the uri is to a ServiceParameterMap
        OntResource or = bookStoreOntModel.getOntResource(uri);
        String toReturn=null;
        for (StmtIterator i = or.listProperties(  ); i.hasNext(); ) {
            Statement s = i.nextStatement();
            Property p = s.getPredicate();
            if (p.canAs(OntProperty.class)){
                Resource r;
                Literal l;
                if (p.getLocalName().equals("paramvalue")){
                    l = s.getLiteral();
                    toReturn=l.getString();
                }
            }
        }               
        return toReturn;
    }
    
    public static void main(String[] args) {

        MashupOntologyReader reader = new MashupOntologyReader();
        Individual[] individuals = reader.getIndividuals("Bookstore");
        reader.obtainServiceInfo(
                               "http://example.com/store#Amazon.com");

    }

}

К сожалению, мы не можем просто считать нужное свойство. Вместо этого нам придется организовать цикл по всем свойствам с помощью метода listProperties() , который снова возвращает Iterator.

Вероятно, вы помните, что в RDF свойства представлены предложениями (или операторами), такими, например, как "The Amazon.com service has an endpoint of http://webservices.amazon.com/onca/xml" (конечная точка сервиса Amazon.com - http://webservices.amazon.com/onca/xml). С точки зрения грамматики фраза "has an endpoint of http://webservices.amazon.com/onca/xml" представляет собой сказуемое предложения. Поэтому первым шагом в извлечении свойства является чтение предложения и извлечение из него сказуемого.

Теперь, когда у нас есть сказуемое, мы должны убедиться в том, что оно на самом деле является свойством онтологии. В конце концов это может быть просто свойство RDF или еще что-либо аномальное. Теперь, когда вы знаете, что это действительно свойство онтологии, мы можем вызвать метод localName и сравнить его с ожидаемым параметром.

Теперь, когда мы знаем, с каким свойством имеем дело, действия с ним зависят от его типа. Некоторые свойства, например, template, являются простыми строковыми литералами, и мы можем извлечь их напрямую. Однако другие свойства, например inputparameter, являются ресурсами, и мы не можем просто извлечь из них литеральное значение. Вместо этого нам придется извлекать их свойства. Например, inputparameter состоит из объекта со свойствами paramname и paramvalue. Чтобы найти эти свойства, мы можем создать методы getParameterName() и getParameterValue() , которые организуют цикл по свойствам указанного ресурса.

При запуске приложения мы получаем результат, похожий на рисунок 2.


Рисунок 2. Информация о сервисе
Информация о сервисе


В начало


Обновленный класс сервиса

От всей этой информации не будет никакой пользы, пока мы не сохраним ее так, чтобы можно было иметь к ней доступ. На текущий момент приложение настроено на анализ и выполнение массива объектов Service, поэтому лучшим решением будет изменить класс Service таким образом, чтобы в него можно было записать данные (см. листинг 17):


Листинг 17. Обновленный класс Service
                    
import java.util.Vector;

public class Service {

       String name = "";
       String baseURL = "";
       String template = "";
       Vector inputParameters = new Vector();
       String queryParameter = "";
       String recordExp = "";
       String xsltTransformationString = "";
       Vector elementValues = new Vector();
       Vector attributeValues = 
new Vector();
       String filterExp = "";
       Service subSvc = null;
   
}

Изначально целью класса Service было предоставление контейнера для данных, создание произвольных объектов и их возвращения. Теперь нам это не нужно - все данные поступают из онтологии. Все, что нам нужно сделать - убедиться, что у нас есть переменные для каждого элемента данных, которые мы желаем сохранить, например, inputparameters и xsltTransformationString.

Кроме того, Чтобы сделать класс более гибким, изменим elementValues и attributeValues на Vector, вместо массивов фиксированного размера, какими они были раньше.

Теперь мы можем наполнить Service.



В начало


Наполнение сервиса

Мы уже проделали всю самую трудную работу по извлечению информации из онтологии. Все, что нужно сделать сейчас - создать новый объект Service и добавить к нему информацию (см. листинг 18):


Листинг 18. Наполнение Service
                    
...
    public Service obtainServiceInfo(String serviceURI){

        Individual bookstore = 
                   bookStoreOntModel.getIndividual(serviceURI);
        
        Service svc = new Service();
        
        for (StmtIterator i = bookstore.listProperties(  );
                                               i.hasNext(); ) {
            
            Statement s = i.nextStatement();
            Property p = s.getPredicate();

            if (p.canAs(OntProperty.class)){
                
                Resource r;
                Literal l;
                if (p.getLocalName().equals("endpoint")){
                    l = s.getLiteral();
                    svc.baseURL=l.getString();
                    System.out.println( p.getLocalName() + " ==> " 
                                                 + l.getString() );
                }
                else if (p.getLocalName().equals("rootoutputnode")){
                    l = s.getLiteral();
                    svc.recordExp=l.getString();
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() );
                }
                else if (p.getLocalName().equals("inputparameter")){
                    r = s.getResource();
                    svc.inputParameters.add(r.getURI());
                    System.out.println( getParameterName(r.getURI())  
                          + " ==> " + getParameterValue(r.getURI()));            
                }
                else if (p.getLocalName()
                          .equals("xsltTransformationString")){
                    l = s.getLiteral();
                    svc.xsltTransformationString=l.getString();
                    System.out.println( p.getLocalName() + " ==> " 
                                                 + l.getString() );
                }
                else if (p.getLocalName().equals("template")){
                    l = s.getLiteral();
                    svc.template=l.getString();
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() ); 
                }
                else if (p.getLocalName().equals("elementValue")){
                    l = s.getLiteral();
                    String[] elementValues = l.getString().split("!");
                    for (int j = elementValues.length; j>0; j--){
                       svc.elementValues.add(elementValues[(j-1)]);
                    }
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() ); 
                }
                else if (p.getLocalName().equals("attributeValue")){
                    l = s.getLiteral();
                    String[] attributeValues = l.getString().split("!");
                    for (int j = attributeValues.length; j>0; j--){
                       svc.attributeValues.add(attributeValues[(j-1)]);
                    }
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() ); 
                }
                else if (p.getLocalName().equals("queryParameter")){
                    l = s.getLiteral();
                    svc.queryParameter = l.getString();
                    System.out.println( p.getLocalName() + " ==> " 
                                                  + l.getString() );
                }
            }
        }
        return svc;
        
    }
   
...

Обратите внимание, что в этом методе мы обрабатываем отличие в названиях между классом Service и онтологией. Пусть, например, мы установили соответствие между endpoint и baseURL. Также стоит отметить, что строковые литералы (например, шаблон) добавляются напрямую в класс, а свойства, представляющие ресурсы, например inputparameter, добавляются не как литералы, а как URI ресурса. (Вы можете принять решение, что для приложения будет лучше хранить всю информацию непосредственно в классе. В этом случае вам придется выполнить здесь дополнительные действия.) Кроме того, мы разделяем значения свойств elementValue и attributeValue в массив, элементы которого будем использовать для наполнения Service.

Следующим шагом будет формирование запроса REST.



В начало


Формирование запроса

Сейчас у нас есть вся информация о сервисе, но у нас нет запроса, который мы могли бы ему послать, поскольку все входные параметры разбиты по отдельным ресурсам. Создание такого запроса зависит от сервиса и не входит в онтологию и сервлет, поэтому мы включим его в класс Service (см. листинг 19):


Листинг 19. Формирование запроса
                    
import java.util.Vector;

public class Service {

       String name = "";
...
   public String getRESTRequest(MashupOntologyReader ont){

       String restQuery = this.baseURL;
       for (int i=0;i<this.inputParameters.size();i++){
           String serviceParameterMapURI = 
                (String) this.inputParameters.elementAt(i);
           if (restQuery.equals(this.baseURL))
               restQuery = restQuery + "?";
           else
               restQuery = restQuery + "&";
           restQuery = restQuery + 
                   ont.getParameterName(serviceParameterMapURI) + "=" +
                   ont.getParameterValue(serviceParameterMapURI);
       }
       
       restQuery = restQuery + "&" + this.queryParameter + "=";

       return restQuery;

   }
}

В методе getRESTRequest() выполняем в цикле обработку каждого из inputParameters, добавляя их к baseURL. И, в заключение, добавляем к концу ссылки параметр запроса и знак равенства (=). Таким образом, когда мы добавляем ключевое слово, по которому будет осуществляться поиск, к концу запроса, мы получаем полную ссылку запроса, например:

http://webservices.amazon.com/onca/xml?ResponseGroup=Large&Service=
   AWSECommerceService&AWSAccess KeyId=000000000000000&SearchIndex=
   Books&Operation=ItemSearch&Title=Star+Wars

Примечание: Приведенный выше код обычно записывается в одну строку. В целях форматирования здесь он показан в несколько строк.



В начало


Выполнение запроса

Фактический запрос подается почти точно так же, как и ранее (см. листинг 20):


Листинг 20. Выполнение запроса
                    
...
    protected void doPost(HttpServletRequest request, 
                          HttpServletResponse response) 
                             throws ServletException, IOException {

        MashupOntologyReader ont = new MashupOntologyReader();
        
        String query = request.getParameter("query").toString();
        String serviceURI = 
                   request.getParameter("serviceURI").toString();
        Service service = ont.obtainServiceInfo(serviceURI);
        
        
        try {
            DocumentBuilder builder = 
                   DocumentBuilderFactory.newInstance()
                                         .newDocumentBuilder();
           
            Service[] svcs = {service};
            
            Document hostDoc = builder.parse(
                     new InputSource(new StringReader("<results/>")));
            Node hostRoot = hostDoc.getDocumentElement();

            for (int k=0; k < svcs.length; k++){

                Service svc = svcs[k];
                Node renderedService =  
                     renderService(svc.getRESTRequest(ont)+query, 
                                                         hostDoc);

                if (renderedService != null){

                    Element nameElement = hostDoc.createElement("h1");
                    nameElement.appendChild(
                                    hostDoc.createTextNode(svc.name));
...

Сначала нам нужно загрузить онтологию - она нужна и для заполнения информации о сервисе, и для создания запроса REST. После того как информация о сервисе заполнена, с ее помощью можно заменить элементы массива services одним элементом. Дальше мы действуем так же, как и раньше, за исключением того, что при вызове метода renderService() нужно создать запрос.

Теперь можно запустить приложение и получить результат, похожий на тот, что получался в более простой версии приложения.



В начало



На предыдущую страницуСтраница 4 из 11 На предыдущую страницу

    IBM в России Конфиденциальность Контакты