package com.sams.jws.chapter15;
/*
 * FileService
 * Obsługuje generowane przez JAXM żądania pobrania pliku, zawarte w komunikacie SOAP.
 * Jeżeli żądanie dotyczy katalogu, usługa zwraca jego zawartość, włącznie z opisem
 * poszczególnych plików w tym katalogu, w ciele komunikatu SOAP. 
 * Jeżeli żądanie dotyczy pobrania zwykłego pliku, jest on zwracany jako załącznik, a w 
 * ciele komunikatu przesyłane są jego parametry. 
 * Typowe żądanie wygląda następująco:
 * <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
 * <Header><CmdName mustUnderstand="1">FileService</CmdName></Header>
 * <Body><FileName>C:\temp\dir\a.txt</FileName></Body>
 * </Envelope>
 */
// Ogólne JDK
import java.io.IOException;
import java.io.File;
import java.util.Iterator;
import java.util.Hashtable;
// JAXM
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPException;
import javax.xml.soap.AttachmentPart;
import javax.xml.messaging.JAXMServlet;
import javax.xml.messaging.ReqRespListener;
// JAF
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
// Serwlet
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;

public class FileService extends JAXMServlet implements ReqRespListener {
    static final boolean DEBUG = true;
    MessageFactory messageFactory = null;
    private ServletContext servletContext = null;
    private static Hashtable errorCodes = new Hashtable();
    static {
        // Inicjalizacja kodów i komunikatów błędów.
        errorCodes.put("100", "Usługa FileService otrzymała niewłaściwe polecenie.");
        errorCodes.put("200", "Brakujący lub niewłaściwy znaczniki FileName.");
        errorCodes.put("300", "Wskazany plik nie istnieje.");
        errorCodes.put("400", "Nie można czytać pliku.");
    }

    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);
        servletContext = servletConfig.getServletContext();
        try {
            messageFactory = MessageFactory.newInstance();
        } catch (SOAPException e) {
            System.err.println(
                "Usługa FileService przechwyciła wyjątek w trakcie tworzenia instancji MessageFactory:");
            e.printStackTrace();
        }
    }  // init()
    /*
     * renderFault
     * Wystąpił błąd. Jako odpowiedź zwracane są dane na jego temat.
     */
    public void renderFault(
        SOAPHeaderElement responseType,
        SOAPBody responseSoapBody,
        String code,
        String param)
        throws SOAPException {

        System.err.println("Kod błędu: " + code);
        System.err.println("Opis błędu: " + errorCodes.get(code) + " " + param);

        responseType.addTextNode("fault");
        SOAPFault soapFault = responseSoapBody.addFault();
        soapFault.setFaultCode(code);
        soapFault.setFaultString((String) errorCodes.get(code) + " " + param);
    }  // renderFault()
    // Wersja metody renderFault() bez dodatkowego parametru (np. nazwy pliku).
    public void renderFault(
        SOAPHeaderElement responseType,
        SOAPBody responseSoapBody,
        String code)
        throws SOAPException {
        renderFault(responseType, responseSoapBody, code, "");
    }  // renderFault()

    /*
     * onMessage
     * Obsługa żądania pobrania pliku przesłanego przez JAXM. 
     */
    public SOAPMessage onMessage(SOAPMessage requestSoapMsg) {

        try {
            if (DEBUG) {
                System.out.println(
                    "Usługa FileService otrzymała następujący komunikat w metodzie onMessage():");
                requestSoapMsg.writeTo(System.out);
                System.out.println("\n");
            }

            // Tworzenie odpowiedzi
            SOAPMessage responseSoapMsg = messageFactory.createMessage();
            SOAPEnvelope responseSoapEnv = responseSoapMsg.getSOAPPart().getEnvelope();
            SOAPHeader responseSoapHdr = responseSoapEnv.getHeader();
            SOAPBody responseSoapBody = responseSoapEnv.getBody();

            SOAPHeaderElement responseType =
                responseSoapHdr.addHeaderElement(responseSoapEnv.createName("ResponseType"));

            // Ekstrakcja treści żądania z walidacją w locie
            SOAPEnvelope requestSoapEnv = requestSoapMsg.getSOAPPart().getEnvelope();
            SOAPHeader requestSoapHdr = requestSoapEnv.getHeader();
            SOAPBody requestSoapBody = requestSoapEnv.getBody();

            // Walidacja nagłówka CmdName 
            String cmd = DocumentUtils.findValueOfChild(requestSoapHdr, "CmdName");
            if ((cmd == null) || (!cmd.equals("FileService"))) {
                // Nie odnaleziono nagłówka CmdName
                renderFault(responseType, responseSoapBody, "100");
                return responseSoapMsg;
            } 

            // Pobranie parametru nazwy pliku
            String fileName = DocumentUtils.findValueOfChild(requestSoapBody, "FileName");
            if ((fileName == null) || (fileName.indexOf("..") > -1)) {
                // Brak parametru lub próba nawigacji poza katalogiem głównym
                renderFault(responseType, responseSoapBody, "200");
                return responseSoapMsg;
            } 

            // Translacja pełnej ścieżki dostępu w odniesieniu do katalogu głównego.
            fileName = servletContext.getRealPath(fileName);
            System.out.println("Opening local fileName: " + fileName);
            File file = new File(fileName);
            if (!file.exists()) {
                // File doesn't exist.
                renderFault(responseType, responseSoapBody, "300", file.getName());
                return responseSoapMsg;
            } 

            if (!file.canRead()) {
                // File is inaccessible.
                renderFault(responseType, responseSoapBody, "400", file.getName());
                return responseSoapMsg;
            } 

            if (file.isDirectory()) {
                // Umieszczenie listy zawartości katalogu w ciele komunikatu SOAP
                responseType.addTextNode("directoryListing");
                SOAPElement listElement = responseSoapBody.addChildElement("List");

                File[] fileList = file.listFiles();
                for (int j = 0; j < fileList.length; j++) {
                    FileElement fileElement = new FileElement(fileList[j]);
                    fileElement.marshalFile(listElement);
                } 
                return responseSoapMsg;

            } 

            if (file.isFile()) {
                // Zwrot zawartości pliku jako załącznika oraz opisu pliku
                // w ciele komunikatu SOAP
                responseType.addTextNode("attachedFile");
                FileElement fileElement = new FileElement(file);
                fileElement.marshalFile(responseSoapBody);

                // Dodanie załącznika do komunikatu
                DataHandler dataHandler = new DataHandler(new FileDataSource(file));
                AttachmentPart attachmentPart =
                    responseSoapMsg.createAttachmentPart(dataHandler);
                attachmentPart.setContentType(dataHandler.getContentType());
                // Add extra MIME header with file name
                attachmentPart.addMimeHeader("FileName", file.getName());
                responseSoapMsg.addAttachmentPart(attachmentPart);
                return responseSoapMsg;

            } 

            // Ogólne wyjątki, mogące pojawić się w trakcie pracy z SOAPMessages. 
        } catch (SOAPException e) {
            System.err.println(
                "Wystąpił błąd usługi FileService w trakcie obsługi lub odpowiadania na komunikat");
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            System.err.println(
                "Wyjątek IOException w usłudze FileService podczas próby zapisu SOAPMessage: ");
            e.printStackTrace();
            return null;
        } 
        return null;
    } // onMessage
} // klasa FileService
