Затем попытался использовать их, используя Python и Ruby. Вот как все это закончилось …
Веб-сервис на Java
Я начал с простого веб-сервиса на Java:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | packagecom.wordpress.jdevel.ws;importjava.io.File;importjava.io.FileFilter;importjava.io.FilenameFilter;importjava.util.ArrayList;importjava.util.List;importjavax.jws.WebService;importjavax.jws.WebMethod;importjavax.jws.WebParam;@WebService(serviceName = "Music")publicclassMusic {    privatestaticfinalFile FOLDER = newFile("D:/TEMP/SONGS");    @WebMethod(operationName = "listSongs")    publicSong[] listSongs(@WebParam(name = "artist") String artist) {        List<Song> songs = newArrayList<Song>();        System.out.println("ARTIST: "+ artist);        if(artist != null) {            File folder = newFile(FOLDER, artist);            if(folder.exists() && folder.isDirectory()) {                File[] listFiles = folder.listFiles(newFilenameFilter() {                    publicbooleanaccept(File dir, String name) {                        returnname.toUpperCase().endsWith(".MP3");                    }                });                for(File file : listFiles) {                    String fileName = file.getName();                    String author = file.getParentFile().getName();                    intsize = (int) (file.length() / 1048576); //Megabytes                    Song song = newSong(fileName, author, size);                    songs.add(song);                }            }        }        returnsongs.toArray(newSong[songs.size()]);    }    @WebMethod(operationName = "listArtists")    publicString[] listArtists() {        File[] folders = getFolders(FOLDER);        List<String> artists = newArrayList<String>(folders.length);        for(File folder : folders) {            artists.add(folder.getName());        }        returnartists.toArray(newString[artists.size()]);    }    privateFile[] getFolders(File parent) {        FileFilter filter = newFileFilter() {            publicbooleanaccept(File pathname) {                returnpathname.isDirectory();            }        };        File[] folders = parent.listFiles(filter);        returnfolders;    }    publicstaticvoidmain(String[] args) {        Music listFiles = newMusic();        String[] artists = listFiles.listArtists();        System.out.println("Artists: "+ artists);        for(String artist : artists) {            Song[] listSongs = listFiles.listSongs(artist);            for(Song song : listSongs) {                System.out.println(song.getArtist() + " : "+ song.getFileName() + " : "+ song.getSize() + "MB");            }        }    }} | 
Нужен также простой bean-компонент для получения более сложных типов:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | packagecom.wordpress.jdevel.ws;importjava.io.Serializable;publicclassSong implementsSerializable {    String fileName;    String artist;    intsize;    publicSong() {    }    publicSong(String fileName, String artist, intsize) {        this.fileName = fileName;        this.artist = artist;        this.size = size;    }    publicString getArtist() {        returnartist;    }    publicvoidsetArtist(String artist) {        this.artist = artist;    }    publicString getFileName() {        returnfileName;    }    publicvoidsetFileName(String fileName) {        this.fileName = fileName;    }    publicintgetSize() {        returnsize;    }    publicvoidsetSize(intsize) {        this.size = size;    }} | 
Он просто перечисляет все подкаталоги в жестко закодированном каталоге FOLDER и рассматривает его как список исполнителей в музыкальной коллекции. Затем вы можете выполнить метод listSongs и получить список mp3-файлов в подпапке Artist.
Чтобы сделать его веб-службой, все, что вам нужно сделать, это аннотировать класс с помощью @WebService (serviceName = «Music»), и каждый метод, который вы хотите представить как операцию веб-службы, должен быть помечен @WebMethod (operationName = «listArtists»).
Это должно быть все, если вы развертываете его на GlassFish, но я использовал Tomcat, поэтому потребовалось еще 3 шага:
  1. Добавьте баночки Metro 2.0 в WEB-INF / lib 
  2. Добавьте Metro сервлет и слушатель в web.xml: 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | <listener>    <listener-class>        com.sun.xml.ws.transport.http.servlet.WSServletContextListener    </listener-class></listener><servlet>    <servlet-name>Music</servlet-name>    <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>    <load-on-startup>1</load-on-startup></servlet><servlet-mapping>    <servlet-name>Music</servlet-name>    <url-pattern>/Music</url-pattern></servlet-mapping> | 
Вы, вероятно, не должны ничего менять здесь. Просто вставьте его в файл web.xml в узле веб-приложения.
3. Добавьте файл sun-jaxws.xml в WEB-INF с объявлением конечной точки:
| 1 2 3 4 | <?xmlversion="1.0"encoding="UTF-8"?>    <endpointimplementation="com.wordpress.jdevel.ws.Music"name="Music"url-pattern="/Music"/></endpoints> | 
- реализация должна соответствовать вашему классу @WebService
- имя должно соответствовать serviceName в аннотации @WebService
- шаблон URL должен соответствовать шаблону URL, который вы объявили в отображении сервлета
Также не должно быть необходимости редактировать эти XML-файлы, если вы создаете их в NetBeans.
Теперь запустите Tomcat и разверните свое приложение. Вы должны иметь возможность получить доступ к своему сервису через что-то вроде
HTTP: // локальный: 8080 / WSServer / Музыка
и увидеть что-то вроде этого:
WSDL будет доступен через
HTTP: // локальный: 8080 / WSServer / Музыка WSDL
Схема для сложных типов:
HTTP: // локальный: 8080 / WSServer / Музыка XSD = 1?
Если у вас это работает, вы можете начать со следующих клиентов.
  Клиент Python 
  Я начал поискать какую-нибудь симпатичную библиотеку веб-сервисов для python и нашел Suds.  Я действительно не использовал ничего подобного.  Внедрение WS-клиента заняло у меня около 15 минут.  Поддержка сложных типов, конечно, и в прошлый раз, когда я использовал Python для чего-то большего, чем 5 строк, было около 3 лет назад.  Вы действительно должны попробовать это. 
Итак, вот код:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | importsudsclassClient:    def __init__(self):    def get_artists(self):        returnself.client.service.listArtists()    def get_songs(self, artist):        returnself.client.service.listSongs(artist)if(__name__ == "__main__"):    client = Client()    artists = client.get_artists()    forartist in artists:        print artist        songs = client.get_songs(artist)        forsong in songs:            print "\t%s : %s : %d%s"% (song.fileName, song.artist, song.size, "MB") | 
Вот и все. Просто, просто. WSDL анализируется, сложные типы генерируются на лету. Что-то красивое. Мне было немного сложнее реализовать что-то подобное в…
  Ruby Client 
  Использование библиотеки SOAP4R.  Просто выполнить 
гем установить soap4r
чтобы получить его (очень нравится этот инструмент). Сначала давайте начнем с кода:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | require 'soap/rpc/driver'require 'soap/wsdlDriver'classClient  def initialize    @driver= factory.create_rpc_driver  end  def get_songs(artist)    songs = @driver.listSongs(:artist => artist)    returnsongs  end  def get_artists    artists = @driver.listArtists(nil)    returnartists  endenddef print_songs(songs)  ifsongs  endendclient = Client.newartists = client.get_artistsartists["return"].each{|artist|  puts artist  songs = client.get_songs(artist)["return"];  songs.each {|song| puts "\t%s : %s : %d%s"% [song.fileName, song.artist, song.size, "MB"]}} | 
Это точно так же. Вызывает веб-сервис для списка исполнителей, а затем, для каждого исполнителя, вызывает mp3-файлы. Затем просто выводит все на консоль.
Мне потребовалось много времени, чтобы заставить его работать. Прежде всего — довольно сложно найти какую-либо документацию. Второе — SOAP4R не работает с ruby 1.9 без небольшого взлома:
http://railsforum.com/viewtopic.php?id=41231
Далее — когда вы не используете WSDL для создания объекта драйвера, результаты немного лучше, но тогда вы точно должны знать, какие сервисы у вас есть, и хотите выполнить. В этом простом примере это не проблема, но если вам нужно сделать его немного более общим … у вас будут проблемы.
Что я имею в виду под «немного лучше»? Сначала код:
| 1 2 3 | @driver= SOAP::RPC::Driver.new("http://localhost:8080/WSServer/Music", "http://ws.jdevel.wordpress.com/");@driver.add_method(ARTISTS_METHOD)@driver.add_method(SONGS_METHOD, "artist") | 
Таким образом, я отвечаю за объявление конечной точки и пространства имен для службы, которую я хочу использовать. Мне также нужно объявить все операции, которые я собираюсь использовать, также с параметрами («автор»). Какая разница? Когда я не использую WSDL, библиотека SOAP4R дает гораздо лучшие типы возвращаемых данных из вызывающих сервисов. Я могу просто опустить [«return»] и получить что-то вроде использования Python.
Что мне действительно нужно знать в Ruby, так это то, как выглядит каждый сложный тип, что делает мою реализацию более чувствительной к изменениям веб-сервиса. Как узнать, какой ключ вы должны использовать для получения данных сложного типа? Проверьте WSDL и найдите операцию, которую хотите вызвать:
| 1 2 3 4 | <operationname="listArtists">    <inputwsam:Action="http://ws.jdevel.wordpress.com/Music/listArtistsRequest"message="tns:listArtists"/>    <outputwsam:Action="http://ws.jdevel.wordpress.com/Music/listArtistsResponse"message="tns:listArtistsResponse"/></operation> | 
Затем найдите выходной комплексный тип в xsd
| 1 2 3 4 5 | <xs:complexTypename="listArtistsResponse">    <xs:sequence>        <xs:elementname="return"type="xs:string"nillable="true"minOccurs="0"maxOccurs="unbounded"/>    </xs:sequence></xs:complexType> | 
Что вам нужно, это значение атрибута имени. В любом случае, обе реализации выглядят действительно хорошо и, что более важно, работают нормально. И в Ruby, и в Python есть отличные библиотеки веб-сервисов, которые обрабатывают сложные типы и разбирают WSDL.
Ссылка: веб-сервисы на Ruby, Python и Java от нашего партнера по JCG в блоге «Истории из мира разработки» .
