The ImageService Web Service

The ImageService Web service is a simple service designed to allow clients to retrieve binary images from the server. There are two methods a client can invoke:

  • Get the names of available images. This method returns the names of images available on the server for retrieval.

  • Get an image with a specified name. This method returns an object representing the image specified by the client by name.

Since SOAP is based on XML, a textual format, a means by which binary information such as an image can be sent using SOAP must be used. A common mechanism is to encode the image using Base64. Base64 is a standard originally developed for sending binary objects in emails and specified in the Multipurpose Internet Mail Extensions (MIME) standard.[3] Base64 provides a means to encode a binary object into a string, and also to decode the string back into a binary object. This is done by treating each set of three 8-bit bytes of binary data as a set of four 6-bit groups, and mapping each group of six bits to a printable subset of the ASCII character set. Each 6-bit number is used to look up the corresponding character in the Base64 alphabet. Table 8.1 shows the Base64 alphabet.

[3] For more information on Base64, refer to the MIME specification http://www.faqs.org/rfcs/rfc2045.html, section 6.8.

The characters thus obtained are concatenated to form the encoded string.[4] The string is decoded back into the binary object by the reverse process. If the decoder comes across a character in the string that is not in the Base64 alphabet, the decoder must ignore the character and continue with the next character.

[4] In MIME, the encoded string is broken into lines of no more than 76 characters each. In SOAP, this is not required and the encoded string can be continuous.

The ImageService interface has two methods:

public String[] getNames(String extension);
public ImageValue getImage(String name);

Table 8.1. The Base64 Alphabet[a]

Value

Encoding

Value

Encoding

Value

Encoding

Value

Encoding

0

A

17

R

34

i

51

z

1

B

18

S

35

j

52

0

2

C

19

T

36

k

53

1

3

D

20

U

37

l

54

2

4

E

21

V

38

m

55

3

5

F

22

W

39

n

56

4

6

G

23

X

40

o

57

5

7

H

24

Y

41

p

58

6

8

I

25

Z

42

q

59

7

9

J

26

a

43

r

60

8

10

K

27

b

44

s

61

9

11

L

28

c

45

t

62

+

12

M

29

d

46

u

63

/

13

N

30

e

47

v

  

14

O

31

f

48

w

(pad)

=

15

P

32

g

49

x

  

16

Q

33

h

50

y

  

[a] This is Table 1 from RFC2045 at http://www.faqs.org/rfcs/rfc2045.html.

The getImage method returns an ImageValue object. This is partly to demonstrate how complex objects (versus primitive types) are sent across SOAP. The object has two attributes for the sake of demonstration: a long, representing the date the image was last modified, and a String, representing the Base64 encoded image.

For the purposes of SOAP serialization, the ImageValue supports a Java bean interface style. That is, it has a constructor with no arguments, and setters and getters for its attributes.

The ImageValue class is defined thus:

package com.javaonpdas.webservices;

public class ImageValue {
  public long dateAsLong;
  public String encodedImage;

  public ImageValue() {
  }

  public ImageValue(long dateAsLong, String encodedImage) {
    this.dateAsLong = dateAsLong;
    this.encodedImage = encodedImage;
  }

  public long getDate() {
    return this.dateAsLong;
  }

  public void setDate(long dateAsLong) {
    this.dateAsLong = dateAsLong;
  }

  public String getEncodedImage() {
    return this.encodedImage;
  }

  public void setEncodedImage(String encodedImage) {
    this.encodedImage = encodedImage;
  }
}

The getNames method returns a String array with the file names of image files with the specified extension in a particular directory. The directory name is hard-coded for the purposes of this example.

The ImageService class is listed in the following.

package com.javaonpdas.webservices;

import java.util.Date;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
public class ImageService {
  private String directory =
    "C:\\JavaOnPDAs\\Desktop\\resources";
  public ImageValue getImage(String name) {
    String encodedImage = null;
    long timeStamp = 0;
    FileInputStream fis = null;
    try {
      // read the file into a byte array
      File imageFile = new File(directory + "\\" + name);
      if (imageFile.exists()) {
        timeStamp = imageFile.lastModified();
        fis = new FileInputStream(imageFile);
        int length = fis.available();
        byte[] rawImage = new byte[length];
        fis.read(rawImage);
        // encode the byte array into a Base64 string
        encodedImage = org.apache.axis.encoding.Base64.encode(
          rawImage);
      }
    }
    catch (Exception e) {
      System.out.println("Error:" + e);
    }
    finally {
      try { if (fis != null) fis.close(); }
      catch (Exception e) {}
    }
    ImageValue image = new ImageValue(timeStamp, encodedImage);
    return image;
  }

  public String[] getNames(String extension) {
    final String ext = extension;
    FilenameFilter filter = new FilenameFilter() {
      public boolean accept(File dir, String name) {
        return name.endsWith(ext);
      }
    };
    File dir = new File(directory);
    String[] files = dir.list(filter);
    return files;
  }
}

Some important points about this code:

  • Since the Web service will be deployed on Axis, we will make use of the built-in Base64 class with a static encode method, org.apache.axis.encoding.Base64.

  • This is a "normal" Java class definition. Apart from making use of the Axis Base64 class (which could be replaced by any Base64 encoder), there is nothing here to suggest that this class will be deployed as a Web service. The publication of this class's methods as a Web service interface is completely separate to the class definition.

Now that we have a class that will serve as our Web service, the next step is to write a Web service deployment descriptor (WSDD). Web service deployment descriptors conform to a standard XSD.

For our ImageService class, the WSDD is quite simple. The WSDD describes:

  • The name of the Web service.

  • The class of the Web service.

  • The methods of the class that are allowed to be accessed.

  • That the service uses the built-in bean serializer for converting an object to a SOAP representation, by accessing its attributes and converting those base types to SOAP format.

  • The name of the class to serialize, using the bean serializer.

The WSDD for the ImageService Web service is deploy-ImageService-AXIS.wsdd, and looks like this:

<deployment xmlns="http://xml.apache.org/axis/wsdd/"
       xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

   service name="ImageService" provider="java:RPC">
      parameter name="className" value="com.javaonpdas.webservices.ImageService"/>
      <parameter name="allowedMethods" value="*"/>
      <beanMapping qname="ns:ImageValue" xmlns:ns="urn:BeanService"
         languageSpecificType= "java:com.javaonpdas.webservices.ImageValue"/>
   </service>
</deployment>

Now we're ready to set up Axis and Tomcat, which we will use to deploy and test the ImageService Web service.