Затем попытался использовать их, используя 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
|
package com.wordpress.jdevel.ws;import java.io.File;import java.io.FileFilter;import java.io.FilenameFilter;import java.util.ArrayList;import java.util.List;import javax.jws.WebService;import javax.jws.WebMethod;import javax.jws.WebParam;@WebService(serviceName = "Music")public class Music { private static final File FOLDER = new File("D:/TEMP/SONGS"); @WebMethod(operationName = "listSongs") public Song[] listSongs(@WebParam(name = "artist") String artist) { List<Song> songs = new ArrayList<Song>(); System.out.println("ARTIST: " + artist); if (artist != null) { File folder = new File(FOLDER, artist); if (folder.exists() && folder.isDirectory()) { File[] listFiles = folder.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.toUpperCase().endsWith(".MP3"); } }); for (File file : listFiles) { String fileName = file.getName(); String author = file.getParentFile().getName(); int size = (int) (file.length() / 1048576); //Megabytes Song song = new Song(fileName, author, size); songs.add(song); } } } return songs.toArray(new Song[songs.size()]); } @WebMethod(operationName = "listArtists") public String[] listArtists() { File[] folders = getFolders(FOLDER); List<String> artists = new ArrayList<String>(folders.length); for (File folder : folders) { artists.add(folder.getName()); } return artists.toArray(new String[artists.size()]); } private File[] getFolders(File parent) { FileFilter filter = new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory(); } }; File[] folders = parent.listFiles(filter); return folders; } public static void main(String[] args) { Music listFiles = new Music(); 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
|
package com.wordpress.jdevel.ws;import java.io.Serializable;public class Song implements Serializable { String fileName; String artist; int size; public Song() { } public Song(String fileName, String artist, int size) { this.fileName = fileName; this.artist = artist; this.size = size; } public String getArtist() { return artist; } public void setArtist(String artist) { this.artist = artist; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public int getSize() { return size; } public void setSize(int size) { 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
|
<?xml version="1.0" encoding="UTF-8"?> <endpoint implementation="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
|
import sudsclass Client: def __init__(self): def get_artists(self): return self.client.service.listArtists() def get_songs(self, artist): return self.client.service.listSongs(artist)if(__name__ == "__main__"): client = Client() artists = client.get_artists() for artist in artists: print artist songs = client.get_songs(artist) for song 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'class Client def initialize @driver = factory.create_rpc_driver end def get_songs(artist) songs = @driver.listSongs(:artist => artist) return songs end def get_artists artists = @driver.listArtists(nil) return artists endenddef print_songs(songs) if songs 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
|
<operation name="listArtists"> <input wsam:Action="http://ws.jdevel.wordpress.com/Music/listArtistsRequest" message="tns:listArtists"/> <output wsam:Action="http://ws.jdevel.wordpress.com/Music/listArtistsResponse" message="tns:listArtistsResponse"/></operation> |
Затем найдите выходной комплексный тип в xsd
|
1
2
3
4
5
|
<xs:complexType name="listArtistsResponse"> <xs:sequence> <xs:element name="return" type="xs:string" nillable="true" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence></xs:complexType> |
Что вам нужно, это значение атрибута имени. В любом случае, обе реализации выглядят действительно хорошо и, что более важно, работают нормально. И в Ruby, и в Python есть отличные библиотеки веб-сервисов, которые обрабатывают сложные типы и разбирают WSDL.
Ссылка: веб-сервисы на Ruby, Python и Java от нашего партнера по JCG в блоге «Истории из мира разработки» .
