cn.buk.api.service.CtripHotelServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for cn.buk.api.service.CtripHotelServiceImpl.java

Source

/*
* Distributable under LGPL v3 license.
* See terms of license at https://github.com/Yunfeng/schotel/blob/master/LICENSE
*/
package cn.buk.api.service;

import cn.buk.api.dto.hotel.*;
import cn.buk.api.dto.hotel.DateRange;
import cn.buk.api.dto.hotel.HotelInfo;
import cn.buk.api.dto.hotel.HotelRatePlan;
import cn.buk.api.util.ConvertUtil;
import cn.buk.hotel.dao.CityDao;
import cn.buk.hotel.dao.HotelDao;
import cn.buk.hotel.dto.DocumentDto;
import cn.buk.hotel.dto.HotelAvailResult;
import cn.buk.hotel.entity.*;
import cn.buk.hotel.entity.Zone;
import cn.buk.qms.AppConfig;
import cn.buk.qms.util.PropertiesUtil;
import cn.buk.util.DateUtil;
import com.ctrip.openapi.java.base.HttpAccessAdapter;
import com.ctrip.openapi.java.utils.ConfigData;
import com.ctrip.openapi.java.utils.SignatureUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.XStreamException;
import com.thoughtworks.xstream.mapper.MapperWrapper;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import org.apache.log4j.Logger;
import org.dom4j.*;
import org.dom4j.io.DocumentResult;
import org.dom4j.io.SAXReader;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
import java.io.StringReader;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
import java.util.*;
import java.util.concurrent.*;

/**
 * User: yfdai
 * Date: 14-9-18
 * Time: ?2:41
 */

public class CtripHotelServiceImpl implements CtripHotelService {

    private static final Logger logger = Logger.getLogger(CtripHotelServiceImpl.class);

    private boolean serviceStopped = false;

    private final String allianceId;
    private final String sid;
    private final String secretKey;
    /**
     * hotelCacheChange,??);
     */
    private final int intervalCacheChange;
    /**
     * ?
     */
    private final int maxDaysOfHotelRate;

    private static final ExecutorService hotelDetailDaoExecutor = Executors.newFixedThreadPool(2);
    private static final ExecutorService hotelCacheChangeDaoExecutor = Executors.newFixedThreadPool(2);
    private static final ExecutorService hotelRatePlanDaoExecutor = Executors.newFixedThreadPool(2);
    private static final ExecutorService hotelCacheChangeDetailExecutor = Executors.newFixedThreadPool(2);

    public CtripHotelServiceImpl(String allianceId, String sid, String secretKey, int intervalCacheChange,
            int maxDaysOfHotelRate) {
        this.allianceId = allianceId;
        this.sid = sid;
        this.secretKey = secretKey;

        this.intervalCacheChange = intervalCacheChange;
        this.maxDaysOfHotelRate = maxDaysOfHotelRate;
    }

    // ?50
    private final int MAX_HOTEL_DETAIL_QUEUE = 50;
    private final BlockingQueue<DocumentDto> hotelDetailQueue = new LinkedBlockingQueue<DocumentDto>(
            MAX_HOTEL_DETAIL_QUEUE);

    private final BlockingQueue<DocumentDto> hotelCacheChangeQueue = new LinkedBlockingQueue<DocumentDto>(100);

    private final BlockingQueue<HotelRatePlanRequestDto> ratePlanRequestQueue = new LinkedBlockingQueue<HotelRatePlanRequestDto>(
            100);
    private final BlockingQueue<DocumentDto> ratePlanQueue = new LinkedBlockingQueue<DocumentDto>(20);

    private static final CacheManager cacheManager = CacheManager.getInstance();

    private CityDao cityDao;
    private HotelDao hotelDao;
    /**
     * ???????
     */
    private int hotelCountPerRequestHotelDetail;

    private static Cache getCache() {
        Cache cache = cacheManager.getCache("openApiCache");
        if (cache == null) {
            cacheManager.addCache("openApiCache");
            cache = cacheManager.getCache("openApiCache");
        }

        return cache;
    }

    private static String execApiRequest(String requestContent, String urlString, String paraName) {
        Proxy proxy = null;
        String proxyUsed = (String) PropertiesUtil.pps.get(AppConfig.PROXY_USED);
        if (proxyUsed.equalsIgnoreCase("1")) {
            String proxyHost = (String) PropertiesUtil.pps.get(AppConfig.PROXY_HOST);
            String proxyPort = (String) PropertiesUtil.pps.get(AppConfig.PROXY_PORT);
            int port = Integer.parseInt(proxyPort);
            SocketAddress address = new InetSocketAddress(proxyHost, port);

            proxy = new Proxy(Proxy.Type.HTTP, address);
        }

        return HttpAccessAdapter.SendRequestToUrl(requestContent, urlString, paraName, proxy);
    }

    private Element createRequestHeaderElement(Document document, String requestType) {
        Element returnElement = document.addElement("Request");
        Element element = returnElement.addElement("Header");
        element.addAttribute("AllianceID", this.allianceId);
        element.addAttribute("SID", this.sid);
        long timestamp = SignatureUtils.GetTimeStamp();
        String signature = null;
        try {
            signature = SignatureUtils.CalculationSignature(timestamp + "", this.allianceId, this.secretKey,
                    this.sid, requestType);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        element.addAttribute("TimeStamp", timestamp + "");
        element.addAttribute("Signature", signature);
        element.addAttribute("RequestType", requestType);

        return returnElement;
    }

    private XStream createXStream() {
        XStream xstream = new XStream() {
            @Override
            protected MapperWrapper wrapMapper(MapperWrapper next) {
                return new MapperWrapper(next) {
                    @Override
                    public boolean shouldSerializeMember(Class definedIn, String fieldName) {
                        //System.out.println("definedIn: " + definedIn.getCanonicalName() + ", Object: " + Object.class.getCanonicalName());
                        if (definedIn == Object.class) {
                            return false;
                        }
                        // System.out.println(fieldName);
                        return super.shouldSerializeMember(definedIn, fieldName);
                    }
                };
            }
        };
        return xstream;
    }

    @Override
    public String importCityInfo(String filename) {
        SAXReader reader = new SAXReader();

        File file = new File(filename);
        if (!file.exists())
            return "not exist.";
        Document document;// ?XML
        try {
            document = reader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
            return "error";
        }

        List flightDataList = document.selectNodes("//CityDetails/*");
        List<City> cities = new ArrayList<City>();
        String temp;
        for (Iterator it = flightDataList.iterator(); it.hasNext();) {
            Element fltElement = (Element) it.next();

            City city = new City();
            cities.add(city);

            city.setCityCode(fltElement.element("CityCode").getText());
            city.setOpenApiId(Integer.parseInt(fltElement.element("City").getText()));
            city.setCityName(fltElement.element("CityName").getText());
            city.setCityEnglishName(fltElement.elementText("CityEName").trim());
            temp = fltElement.elementText("Country").trim();
            if (temp.equalsIgnoreCase("1"))
                city.setCountryCode("CN");
            else
                city.setCountryCode(temp);
            city.setProvinceId(Integer.parseInt(fltElement.elementText("Province")));
            city.setAirport(fltElement.element("Airport").getText());
        }

        int count = 0;
        for (City city : cities) {
            if (city.getCityCode() == null || city.getCityCode().trim().length() == 0)
                continue;
            if (cityDao.create(city) == 1)
                count++;
        }

        return "create " + count + " record(s).";
    }

    @Override
    public String importDistrictInfo(String filename) {
        SAXReader reader = new SAXReader();

        File file = new File(filename);
        if (!file.exists())
            return "not exist.";
        Document document;// ?XML
        try {
            document = reader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
            return "error";
        }

        List list = document.selectNodes("//LocationDetails/*");
        List<District> districts = new ArrayList<District>();
        for (Iterator it = list.iterator(); it.hasNext();) {
            Element element = (Element) it.next();

            District district = new District();
            districts.add(district);

            district.setName(element.element("LocationName").getText());
            district.setEnglishName(element.element("LocationEName").getText());
            district.setDistrictId(Integer.parseInt(element.element("Location").getText()));
            district.setCityId(Integer.parseInt(element.element("LocationCity").getText()));
        }

        int count = 0;
        for (District district : districts) {
            if (hotelDao.createDistrict(district) == 1)
                count++;
        }

        return "create " + count + " record(s).";
    }

    @Override
    public String importZoneInfo(String filename) {
        SAXReader reader = new SAXReader();

        File file = new File(filename);
        if (!file.exists())
            return "not exist.";
        Document document;// ?XML
        try {
            document = reader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
            return "error";
        }

        List list = document.selectNodes("//ZoneDetails/*");
        List<Zone> zones = new ArrayList<Zone>();
        int maxLength = 0;
        for (Iterator it = list.iterator(); it.hasNext();) {
            Element element = (Element) it.next();

            Zone zone = new Zone();
            zones.add(zone);

            zone.setZoneId(Integer.parseInt(element.element("Zone").getText()));
            zone.setCityId(Integer.parseInt(element.element("City").getText()));
            zone.setName(element.element("ZoneName").getText());
            zone.setEnglishName(element.element("ZoneEName").getText());
            zone.setDescription(element.element("ZoneDesc").getText());
            if (zone.getDescription().length() > maxLength)
                maxLength = zone.getDescription().length();
            zone.setMapUse(element.element("ZoneMapuse").getText());
            zone.setMapRange(element.element("ZoneRange").getText());
            zone.setMapPic(element.element("ZoneMapPic").getText());
        }

        int count = 0;
        for (Zone zone : zones) {
            if (hotelDao.createZone(zone) == 1)
                count++;
        }
        System.out.println(maxLength);

        return "create " + count + " record(s).";
    }

    /**
     * ??
     * @param cityId ?
     * @return ??
     */
    @Override
    public String searchHotel(int cityId) {
        if (cityId <= 0)
            return "ER#CITYID IS " + cityId;

        //headerAPI?
        Cache cache = getCache();
        String cacheKey = ConfigData.OTA_HotelSearch_Request;
        net.sf.ehcache.Element cacheElement = cache.get(cacheKey);
        if (cacheElement != null) {
            Element headerElement = (Element) cacheElement.getValue();

            int accessCount = Integer.parseInt(headerElement.attributeValue("AccessCount"));
            int currentCount = Integer.parseInt(headerElement.attributeValue("CurrentCount")) + 1;
            logger.info("AccessCount=" + headerElement.attributeValue("AccessCount") + ", CurrentCount="
                    + headerElement.attributeValue("CurrentCount") + ", ResetTime="
                    + headerElement.attributeValue("ResetTime"));
            if (currentCount >= accessCount) {
                try {
                    logger.info("Sleep for one minute.");
                    Thread.sleep(60000);
                } catch (InterruptedException ex) {
                    logger.warn(Thread.currentThread().getName() + " is interrupted.");
                    return "ER#Thread.sleep is interrupted.";
                }
            }
        }

        HotelRequestBody request = new HotelRequestBody();
        request.createHotelRequestRQ();
        request.getHotelRequestRQ().getCriteria().getCriterion().getHotelRef().setHotelCityCode(cityId);
        String xml;

        try {
            JAXBContext jc = JAXBContext.newInstance(HotelRequestBody.class);

            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            m.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

            DocumentResult documentResult = new DocumentResult();
            m.marshal(request, documentResult);

            org.dom4j.Document document = documentResult.getDocument();
            org.dom4j.Element requestElement = document.getRootElement();
            Element ele = requestElement.element("OTA_HotelSearchRQ");
            ele.addNamespace("", "http://www.opentravel.org/OTA/2003/05");

            org.dom4j.Document doc1 = DocumentHelper.createDocument();

            org.dom4j.Element rootElement = createRequestHeaderElement(doc1, ConfigData.OTA_HotelSearch_Request);
            rootElement.add(requestElement);

            xml = doc1.asXML();
        } catch (JAXBException ex) {
            logger.error(ex.getMessage());
            return "ER";
        }

        if (serviceStopped)
            return "ER#Service stopped.";
        String response = execApiRequest(xml, ConfigData.OTA_HotelSearch_Url, "requestXML");
        //?
        SAXReader reader = new SAXReader();

        Document document;// ?XML
        try {
            document = reader.read(new StringReader(response));

            if (serviceStopped)
                return "ER#Service stopped.";
            response = processHotelSearchResponse(document);
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            return "ER";
        }

        return response;
    }

    private String processHotelSearchResponse(Document document) {
        if (document == null)
            return "ER#Document is null.";

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            logger.error(document.asXML());
            return "ER#ResultCode is not Success.";
        }

        //header?
        getCache().put(new net.sf.ehcache.Element(ConfigData.OTA_HotelSearch_Request, headerElement));

        List myNodes = document.selectNodes("/Response/HotelResponse/*");
        Element eleOtaHotelSearchRS = (Element) myNodes.get(0);

        XStream xs = createXStream();
        xs.alias("Response", HotelSearchResponse.class);
        xs.processAnnotations(HotelSearchResponse.class);

        Date date0 = DateUtil.getCurDateTime();
        String xml = eleOtaHotelSearchRS.asXML();
        HotelSearchResponse response = (HotelSearchResponse) xs.fromXML(xml);
        int spanMilliSeconds = DateUtil.getPastTime(date0);
        logger.info("xml -> object: " + spanMilliSeconds + "ms.");
        int total = response.getHotelInfos().size();
        if (total == 0) {
            //logger.info(xml);
            if (response.getSuccess() != null) {
                logger.info("There is no hotel.");
                return "OK#0 hotel";
            }
        }

        // return response;
        String rs;
        int saveCount = 0;
        int count = 0;
        for (HotelInfo hotelInfo : response.getHotelInfos()) {
            if (this.serviceStopped)
                break;

            cn.buk.hotel.entity.HotelInfo hotelInfo1 = ConvertUtil.convertHotelInfo(hotelInfo);

            int retCode = hotelDao.createHotelInfo(hotelInfo1);

            if (retCode == 1 || retCode == 2) {
                saveCount++;
            }

            count++;
            if (count % 100 == 0) {
                logger.info("Progress: " + count + " / " + total);
            }
        }

        if (saveCount > 0)
            rs = "OK#" + saveCount + " be saved";
        else
            rs = "ER";

        return rs;
    }

    private String processHotelDetailResponse(Document document) {
        if (document == null)
            return "ER#Document is null.";

        //?????
        Map uris = new HashMap();
        uris.put("ns", "http://www.opentravel.org/OTA/2003/05");
        XPath xpath = document.createXPath("//ns:HotelDescriptiveContents/*");
        xpath.setNamespaceURIs(uris);
        List myNodes = xpath.selectNodes(document);

        String rs = "HAHA";

        if (myNodes.size() == 0) {
            return "ER#There is nothing.";
        }

        logger.debug(Thread.currentThread().getName() + " is on processHotelDetailResponse.");

        XStream xs = createXStream();
        xs.alias("HotelDescriptiveContent", HotelDescriptiveContent.class);
        xs.processAnnotations(HotelDescriptiveContent.class);

        String xml;

        cn.buk.hotel.entity.HotelInfo hotelInfo1;
        String hotelCode, hotelName;
        for (Iterator it = myNodes.iterator(); it.hasNext();) {
            if (serviceStopped)
                break;

            Element element = (Element) it.next();

            xml = element.asXML();
            HotelDescriptiveContent response = (HotelDescriptiveContent) xs.fromXML(xml);

            hotelCode = response.getHotelCode();
            hotelName = response.getHotelName();
            hotelInfo1 = hotelDao.getHotelDetailInfoByHotelCode(hotelCode);

            if (hotelInfo1 == null) {
                rs = "ER#No hotel is found with hotelCode(" + hotelCode + ")";
                logger.warn(rs);
                return rs;
            } else {
                try {
                    if (response.getHotelInfo() != null && response.getHotelInfo().getWhenBuilt() != null)
                        hotelInfo1.setBuildDate(DateUtil.convertToDate(response.getHotelInfo().getWhenBuilt()));
                } catch (Exception e) {
                    logger.error(xml);
                    logger.error(e.getMessage());
                    return "ER#WhenBuild: ";// + response.getHotelInfo().getWhenBuilt();
                }

                if (response.getHotelInfo() != null && response.getHotelInfo().getSegmentCategories() != null
                        && response.getHotelInfo().getSegmentCategories().size() > 0) {
                    int consumerLevel = Integer
                            .parseInt(response.getHotelInfo().getSegmentCategories().get(0).getCode());
                    hotelInfo1.setConsumerLevel(consumerLevel);
                    if (response.getHotelInfo().getSegmentCategories().size() > 1) {
                        logger.warn(hotelCode + "," + hotelName + ": SegmentCategory\'s size is "
                                + response.getHotelInfo().getSegmentCategories().size());
                    }
                }

                if (response.getHotelInfo() != null && response.getHotelInfo().getAddress().getZones() != null) {
                    if (hotelInfo1.getHotelAddressZones() == null)
                        hotelInfo1.setHotelAddressZones(new ArrayList<HotelAddressZone>());
                    for (cn.buk.api.dto.hotel.Zone zone : response.getHotelInfo().getAddress().getZones()) {
                        if (zone.getZoneCode() > 0) {
                            HotelAddressZone zone1 = new HotelAddressZone();
                            zone1.setHotelInfo(hotelInfo1);

                            zone1.setZoneCode(zone.getZoneCode());
                            zone1.setZoneName(zone.getZoneName());

                            hotelInfo1.addAddressZone(zone1);
                        }
                    }
                }

                //SERVICE
                if (response.getHotelInfo() != null && response.getHotelInfo().getHotelInfoServices() != null) {
                    if (hotelInfo1.getHotelServices() == null)
                        hotelInfo1.setHotelServices(new ArrayList<HotelServiceInfo>());
                    for (HotelInfoService service : response.getHotelInfo().getHotelInfoServices()) {
                        HotelServiceInfo info = new HotelServiceInfo();
                        info.setHotelInfo(hotelInfo1);

                        info.setServiceCode(service.getCode());
                        info.setServiceId(service.getId());
                        info.setServiceDesc(service.getDescriptiveText());

                        hotelInfo1.addHotelService(info);
                    }
                }
                //GuestRoom???
                if (hotelInfo1.getGuestRooms() == null)
                    hotelInfo1.setGuestRooms(new ArrayList<HotelGuestRoom>());
                else
                    hotelInfo1.getGuestRooms().clear();
                if (response.getFacilityInfo() != null && response.getFacilityInfo().getGuestRooms() != null
                        && response.getFacilityInfo().getGuestRooms().size() > 0) {

                    for (GuestRoom guestRoom : response.getFacilityInfo().getGuestRooms()) {
                        HotelGuestRoom info = new HotelGuestRoom();
                        info.setHotelInfo(hotelInfo1);
                        //hotelInfo1.getGuestRooms().add(info);

                        info.setRoomTypeName(guestRoom.getRoomTypeName());
                        RoomType roomType = guestRoom.getRoomType();
                        info.setRoomTypeCode(roomType.getRoomTypeCod());
                        info.setStandardOccupancy(roomType.getStandardOccupancy());
                        info.setSize(roomType.getSize());
                        info.setFloor(roomType.getFloor());
                        info.setInvBlockCode(roomType.getInvBlockCode());
                        info.setBedTypeCode(roomType.getBedTypeCode());
                        if (roomType.isNonSmoking())
                            info.setNonSmoking(1);
                        else
                            info.setNonSmoking(0);
                        info.setHasWindow(roomType.getHasWindow());
                        info.setQuantity(roomType.getQuantity());
                        info.setRoomSize(roomType.getRoomSize());

                        if (guestRoom.getAmenities() != null) {
                            for (Amenity amenity : guestRoom.getAmenities()) {
                                HotelGuestRoomAmenity amenity1 = new HotelGuestRoomAmenity();
                                amenity1.setHotelGuestRoom(info);
                                if (info.getHotelGuestRoomAmenities() == null)
                                    info.setHotelGuestRoomAmenities(new ArrayList<HotelGuestRoomAmenity>());
                                info.getHotelGuestRoomAmenities().add(amenity1);

                                amenity1.setCode(amenity.getRoomAmenityCode());
                                amenity1.setDescription(amenity.getDescriptiveText());
                            }
                        }
                        hotelInfo1.addGuestRoom(info);
                    }
                }

                //ref points begin
                if (response.getAreaInfo() != null && response.getAreaInfo().getRefPoints() != null) {
                    if (hotelInfo1.getRefPoints() == null)
                        hotelInfo1.setRefPoints(new ArrayList<HotelRefPoint>());
                    for (RefPoint point : response.getAreaInfo().getRefPoints()) {
                        HotelRefPoint point1 = new HotelRefPoint();
                        point1.setHotelInfo(hotelInfo1);
                        hotelInfo1.getRefPoints().add(point1);

                        point1.setDistance(point.getDistance());
                        point1.setUnitOfMeasureCode(point.getUnitOfMeasureCode());
                        point1.setName(point.getName());
                        point1.setRefPointCategoryCode(Integer.parseInt(point.getRefPointCategoryCode()));
                        point1.setRefPointName(point.getRefPointName());
                        point1.setLatitude(point.getLatitude());
                        point1.setLongitude(point.getLongitude());
                        point1.setDescription(point.getDescriptiveText());
                    }
                }
                //ref points end

                //MultimediaDescriptions begin
                if (response.getMultimediaDescriptions() != null) {
                    if (hotelInfo1.getMedias() == null)
                        hotelInfo1.setMedias(new ArrayList<HotelMultimediaInfo>());
                    for (MultimediaDescription md : response.getMultimediaDescriptions()) {
                        if (md.getImageItems() != null) {
                            for (ImageItem item : md.getImageItems()) {
                                HotelMultimediaInfo info = new HotelMultimediaInfo();
                                info.setHotelInfo(hotelInfo1);
                                hotelInfo1.getMedias().add(info);

                                info.setMediaType("image");
                                info.setCategory(Integer.parseInt(item.getCategory()));
                                info.setCaption(item.getImageDescription().getCaption());
                                info.setUrl(item.getImageFormat().getUrl());
                            }
                        }
                        if (md.getTextItems() != null) {
                            for (TextItem item : md.getTextItems()) {
                                HotelMultimediaInfo info = new HotelMultimediaInfo();
                                info.setHotelInfo(hotelInfo1);
                                hotelInfo1.getMedias().add(info);

                                info.setMediaType("text");
                                info.setCategory(Integer.parseInt(item.getCategory()));
                                info.setUrl(item.getUrl());
                                info.setDescription(item.getDescription());
                            }
                        }
                    }
                }

                //save
                int retCode = hotelDao.updateHotelInfo(hotelInfo1);

                if (retCode == 1) {
                    rs = "OK#Save OK";
                    //cache
                    CacheHotel cacheHotel = hotelDao.getCacheHotel(hotelCode);

                    if (cacheHotel == null) {
                        cacheHotel = new CacheHotel();
                        cacheHotel.setHotelCode(hotelCode);

                        hotelDao.createCacheHotel(cacheHotel);
                    } else {
                        cacheHotel.setCacheTime(DateUtil.getCurDateTime());
                        hotelDao.updateCacheHotel(cacheHotel);
                    }
                } else {
                    logger.info(xml);
                    rs = "ER#Save Status is " + retCode + " for hotelCode[" + hotelCode + "]";
                    return rs;
                }
            }
        } //end for

        return rs;
    }

    private void executeSaveRatePlanDocument(final Document document, final int periodId,
            final List<String> hotelCodes) {
        //?????
        Map uris = new HashMap();
        uris.put("ns", "http://www.opentravel.org/OTA/2003/05");
        XPath xpath = document.createXPath("//ns:OTA_HotelRatePlanRS/*");
        xpath.setNamespaceURIs(uris);
        List myNodes = xpath.selectNodes(document);

        if (myNodes.size() == 0) {
            logger.warn("myNodes.size() = 0, " + hotelCodes.toString());
            return;
        }

        if (myNodes.size() == 1) {
            for (String hotelCode : hotelCodes) {
                if (periodId == 1) {
                    logger.info("set [" + hotelCode + "]\'s rateplan status is -1.");
                    hotelDao.setHotelRatePlanStatusByHotelCode(hotelCode, -1);
                    hotelDao.setCacheRatePlanDone(hotelCode, periodId);
                }
            }
            logger.info(
                    "myNodes.size() = 1, " + periodId + ", " + hotelCodes.size() + ", " + hotelCodes.toString());
            return;
        }

        Date baseTime0 = DateUtil.getCurDateTime();
        for (Iterator it = myNodes.iterator(); it.hasNext();) {
            if (serviceStopped)
                break;

            Element element = (Element) it.next();
            if (!element.getName().equalsIgnoreCase("RatePlans"))
                continue;

            XStream xs = createXStream();
            xs.alias("RatePlans", HotelRatePlans.class);
            xs.processAnnotations(HotelRatePlans.class);

            String xml = element.asXML();
            HotelRatePlans response = null;
            try {
                response = (HotelRatePlans) xs.fromXML(xml);
            } catch (XStreamException ex) {
                //TODO ????? yfdai 2015-1-13
                logger.error(ex.getMessage());
            }

            if (response == null)
                break;

            String hotelCode = response.getHotelCode();
            cn.buk.hotel.entity.HotelInfo hotelInfo1;

            if (response.getHotelRatePlans() == null || response.getHotelRatePlans().size() == 0) {
                if (periodId == 1) {
                    logger.info("set [" + hotelCode + "]\'s rateplan status is -1.");
                    hotelDao.setHotelRatePlanStatusByHotelCode(hotelCode, -1);
                }
                logger.info("OK#There is no rates for {" + hotelCode + "," + periodId + "}.");
            } else {
                cn.buk.hotel.entity.HotelRatePlan hotelRatePlan;
                hotelInfo1 = hotelDao.getHotelInfoByHotelCode(hotelCode);
                if (hotelInfo1 == null) {
                    logger.warn("There is not hotel info for [" + hotelCode + "]");
                    continue;
                }

                //?RatePlan?
                List<cn.buk.hotel.entity.HotelRatePlan> ratePlans = new ArrayList<cn.buk.hotel.entity.HotelRatePlan>();
                for (HotelRatePlan dtoRatePlan : response.getHotelRatePlans()) {
                    //if (serviceStopped) break;

                    try {
                        hotelRatePlan = ConvertUtil.convertHotelRatePlan(dtoRatePlan);
                        hotelRatePlan.setHotelInfo(hotelInfo1);

                        ratePlans.add(hotelRatePlan);

                        //                        int retCode;
                        //                        Date timeBegin = DateUtil.getCurDateTime();
                        //                        retCode = hotelDao.createHotelRatePlan(hotelRatePlan);
                        //                        int spans = DateUtil.getPastTime(timeBegin);
                        //                        if (spans> 1000) logger.warn("CreateHotelRatePlan\'s time is " + Integer.toString(spans) + " ms.");
                        //                        if (retCode != 1 && retCode != 2) {
                        //                            logger.error(xml);
                        //                            logger.error("save failed.");
                        //                            return;
                        //                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        logger.error(xml);
                        logger.error(ex.getMessage());
                        return;
                    }
                } // end for

                int retCode;
                Date timeBegin = DateUtil.getCurDateTime();
                retCode = hotelDao.createHotelRatePlans(ratePlans);
                int spans = DateUtil.getPastTime(timeBegin);
                if (spans > 1000)
                    logger.warn("CreateHotelRatePlans\'s time is " + Integer.toString(spans) + " ms.");
                if (retCode != 1 && retCode != 2) {
                    logger.error(xml);
                    logger.error("save failed.");
                    return;
                }

                hotelDao.setHotelRatePlanStatusByHotelCode(hotelCode, 1);
            }

            //cache
            if (periodId > 0)
                hotelDao.setCacheRatePlanDone(hotelCode, periodId);
        } // end for
        int spanTotal = DateUtil.getPastTime(baseTime0);
        logger.debug(String.format("total time : %d ms", spanTotal));
    }

    /**
     * ?HotelRatePlanResponse
     * @param document ??
     * @return ??
     */
    private String processHotelRatePlanResponse(final Document document, final int periodId,
            final List<String> hotelCodes) {
        if (document == null)
            return "ER#Document is null.";

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            //logger.error("ER#ResultCode is not Success.");
            logger.error(document.asXML());
            return "ER#ResultCode is not Success.";
        }

        DocumentDto documentDto = new DocumentDto();
        documentDto.setDocument(document);
        documentDto.setPeriodId(periodId);

        for (String hotelCode : hotelCodes)
            documentDto.getHotelCodes().add(hotelCode);

        try {
            logger.debug("Current ratePlanQueue\'s size: " + ratePlanQueue.size());
            logger.debug("put hotelCodes\'s size is " + hotelCodes.size());

            ratePlanQueue.put(documentDto);
            hotelRatePlanDaoExecutor.execute(new HotelRatePlanDaoThread());
        } catch (InterruptedException e) {
            logger.warn(Thread.currentThread().getName() + " is interrupted.");
            return "ER#RatePlanQueue.put is interrupted.";
        }

        return "OK#Document has been added to Queue.";
    }

    private String processHotelCacheChangeResponse(final Document document) {
        if (document == null)
            return "ER#Document is null.";

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            logger.error(document.asXML());
            return "ER#ResultCode is not Success.";
        }

        //header?
        getCache().put(new net.sf.ehcache.Element(ConfigData.OTA_HotelCacheChange_Request, headerElement));

        DocumentDto documentDto = new DocumentDto();
        documentDto.setDocument(document);
        try {
            hotelCacheChangeDaoExecutor.execute(new HotelCacheChangeDaoThread());

            logger.debug("Current hotelCacheChangeQueue\'s size is " + hotelCacheChangeQueue.size());
            hotelCacheChangeQueue.put(documentDto);
        } catch (InterruptedException e) {
            logger.warn(Thread.currentThread().getName() + " is interrupted.");
            return "ER#HotelCacheChangeQueue.put is interrupted.";
        }

        return "OK#HotelCacheChange document has been added into queue.";
    }

    @Override
    public String importHotelSearchRS(String filename) {
        String rs;
        SAXReader reader = new SAXReader();

        logger.info("filename: " + filename);

        File file = new File(filename);
        if (!file.exists())
            return null;

        Document document;// ?XML
        try {
            document = reader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
            return "error";
        }

        rs = processHotelSearchResponse(document);

        return rs;
    }

    @Override
    public synchronized String searchHotelDetail(List<String> hotelCodes, boolean returnXml) {
        if (hotelCodes == null || hotelCodes.size() == 0)
            return "ER#hotelcodes is null";

        //headerAPI?
        net.sf.ehcache.Element cacheElement = getCache().get(ConfigData.OTA_HotelDetail_Request);
        if (cacheElement != null) {
            Element headerElement = (Element) cacheElement.getValue();
            int accessCount = Integer.parseInt(headerElement.attributeValue("AccessCount"));
            int currentCount = Integer.parseInt(headerElement.attributeValue("CurrentCount")) + 1;
            logger.info(ConfigData.OTA_HotelDetail_Request + " AccessCount="
                    + headerElement.attributeValue("AccessCount") + ", CurrentCount="
                    + headerElement.attributeValue("CurrentCount") + ", ResetTime="
                    + headerElement.attributeValue("ResetTime"));
            if (currentCount >= accessCount) {
                try {
                    logger.info("Sleep for one minute.");
                    Thread.sleep(60000);
                } catch (InterruptedException ex) {
                    logger.warn(Thread.currentThread().getName() + " is interrupted.");
                    return "ER#SearchHotelDetail thread.sleep is interrupted.";
                }
            }
        }

        if (this.serviceStopped)
            return "ER#Service stopped.";

        HotelRequestBody request = new HotelRequestBody();
        request.createHotelDetailRequest();
        for (String hotelCode : hotelCodes) {
            if (this.serviceStopped)
                return "ER#Service stopped.";

            HotelDescriptiveInfo hotelDescriptiveInfo = new HotelDescriptiveInfo();
            hotelDescriptiveInfo.setHotelCode(hotelCode);

            cn.buk.hotel.entity.HotelInfo hotelInfo = hotelDao.getHotelDetailInfoByHotelCode(hotelCode);
            if (hotelInfo != null) {
                //if (hotelInfo.getGuestRooms().size() == 0) {
                hotelDescriptiveInfo.setHotelInfoFacilityInfo(new HotelInfoFacilityInfo());
                hotelDescriptiveInfo.getHotelInfoFacilityInfo().setSendGuestRooms(true);
                //}

                if (hotelInfo.getRefPoints().size() == 0) {
                    hotelDescriptiveInfo.setHotelInfoAreaInfo(new HotelInfoAreaInfo());
                    hotelDescriptiveInfo.getHotelInfoAreaInfo().setSendAttractions(true);
                    hotelDescriptiveInfo.getHotelInfoAreaInfo().setSendRecreations(true);
                }

                if (hotelInfo.getMedias().size() == 0) {
                    hotelDescriptiveInfo.setHotelInfoMultimedia(new HotelInfoMultimedia());
                    hotelDescriptiveInfo.getHotelInfoMultimedia().setSendData(true);
                }
            }

            request.getHotelDetailRequest().getHotelDescriptiveInfos().add(hotelDescriptiveInfo);
        }

        String requestXml;

        try {
            JAXBContext jc = JAXBContext.newInstance(HotelRequestBody.class);

            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            m.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

            DocumentResult documentResult = new DocumentResult();
            m.marshal(request, documentResult);

            org.dom4j.Document document = documentResult.getDocument();
            org.dom4j.Element requestElement = document.getRootElement();

            Element ele = requestElement.element("OTA_HotelDescriptiveInfoRQ");
            ele.addNamespace("", "http://www.opentravel.org/OTA/2003/05");

            org.dom4j.Document doc1 = DocumentHelper.createDocument();

            if (this.serviceStopped)
                return "ER#Service stopped.";

            org.dom4j.Element rootElement = createRequestHeaderElement(doc1, ConfigData.OTA_HotelDetail_Request);
            rootElement.add(requestElement);

            requestXml = doc1.asXML();
        } catch (JAXBException e) {
            e.printStackTrace();
            return "ER#OTA_exception";
        }

        Date date0 = DateUtil.getCurDateTime();

        logger.debug(ConfigData.OTA_HotelDetail_Request + ": begin");
        logger.debug(requestXml);

        if (this.serviceStopped)
            return "ER#Service stopped.";
        String response = execApiRequest(requestXml, ConfigData.OTA_HotelDetail_Url, "requestXML");

        logger.debug(response);
        int apiElapsedTime = DateUtil.getPastTime(date0);
        logger.debug(ConfigData.OTA_HotelDetail_Request + ": end, " + apiElapsedTime + "ms");

        if (returnXml)
            return response;

        //?
        String rs;
        SAXReader reader = new SAXReader();
        Document document;// ?XML
        try {
            document = reader.read(new StringReader(response));
            Element headerElement = document.getRootElement().element("Header");

            //header?
            getCache().put(new net.sf.ehcache.Element(ConfigData.OTA_HotelDetail_Request, headerElement));

            if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
                logger.error(requestXml);
                logger.error(document.asXML());
                return "ER#ResultCode is not Success.";
            }

            DocumentDto documentDto = new DocumentDto();
            documentDto.setDocument(document);

            if (hotelDetailQueue.size() == MAX_HOTEL_DETAIL_QUEUE) {
                logger.warn("hotelDetailQueue is full, thread will be blocked here.");
            }
            hotelDetailQueue.put(documentDto);
            hotelDetailDaoExecutor.execute(new HotelDetailDaoThread());

            rs = "OK#save to the queue.";
        } catch (InterruptedException ex) {
            return "ER#Interrupt occured.";
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            return "ER#searchHotelDetail";
        }

        return rs;
    }

    @Override
    public String searchHotelRatePlan(final List<String> hotelCodes, final int periodId, final boolean returnXml) {
        if (hotelCodes == null || hotelCodes.size() == 0)
            return "ER#hotelcodes is null or empty.";

        int minDay = (periodId - 1) * 28;
        int maxDay = periodId * 28;
        if (maxDay > 90)
            maxDay = 90;

        if (minDay >= this.maxDaysOfHotelRate)
            return "ER#Exceed the max day of rate!";
        if (maxDay > this.maxDaysOfHotelRate)
            maxDay = this.maxDaysOfHotelRate;

        Date startDate = DateUtil.add(DateUtil.getCurDate(), minDay);
        Date endDate = DateUtil.add(DateUtil.getCurDate(), maxDay);

        String response = execSearchHotelRatePlan(hotelCodes, startDate, endDate);
        if (this.serviceStopped)
            return "ER#EXIT";

        //?
        String rs;
        SAXReader reader = new SAXReader();
        Document document;// ?XML
        try {
            document = reader.read(new StringReader(response));
        } catch (DocumentException e) {
            logger.error(e.getMessage());
            return "ER#xml->document";
        }

        try {
            rs = processHotelRatePlanResponse(document, periodId, hotelCodes);
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            logger.debug(response);
            return "ER#processHotelRatePlanResponse";
        }

        return rs;
    }

    @Override
    public String searchHotelRatePlan(final String hotelCode, Date startDate, Date endDate) {
        return searchHotelRatePlan(hotelCode, startDate, endDate, false);
    }

    @Override
    public String searchHotelRatePlan(final String hotelCode, Date startDate, Date endDate,
            final boolean returnXml) {
        List<String> hotelCodes = new ArrayList<String>();
        hotelCodes.add(hotelCode);
        String response = execSearchHotelRatePlan(hotelCodes, startDate, endDate);
        if (returnXml)
            return response;

        //?
        String rs = "rs";
        SAXReader reader = new SAXReader();
        Document document;// ?XML
        try {
            document = reader.read(new StringReader(response));
        } catch (DocumentException e) {
            logger.error(e.getMessage());
            return "ER#xml->document";
        }

        if (document == null)
            return "ER#Document is null.";

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            //logger.error("ER#ResultCode is not Success.");
            logger.error(document.asXML());
            return "ER#ResultCode is not Success.";
        }

        rs = processHotelRatePlanResponse(document, 0, hotelCodes);

        return rs;
    }

    private synchronized String doSearchHotelRatePlan(String requestXml) {

        logger.debug("doSearchHotelRatePlan begin..." + Thread.currentThread().getName());

        //headerAPI?
        String cacheKey = ConfigData.OTA_HotelRatePlan_Request;
        net.sf.ehcache.Element cacheElement = getCache().get(cacheKey);
        if (cacheElement != null) {
            Element headerElement = (Element) cacheElement.getValue();
            int accessCount = Integer.parseInt(headerElement.attributeValue("AccessCount"));
            int currentCount = Integer.parseInt(headerElement.attributeValue("CurrentCount")) + 1;
            logger.info(ConfigData.OTA_HotelRatePlan_Request + " AccessCount="
                    + headerElement.attributeValue("AccessCount") + ", CurrentCount="
                    + headerElement.attributeValue("CurrentCount") + ", ResetTime="
                    + headerElement.attributeValue("ResetTime"));
            if (currentCount >= accessCount) {
                try {
                    logger.info("Sleep for one minute.");
                    Thread.sleep(60000);
                } catch (InterruptedException ex) {
                    logger.warn(Thread.currentThread().getName() + " is interrupted.");
                    return "ER#DoSearchHotelRatePlan thread.sleep is interrupted.";
                }
            }
        }
        if (this.serviceStopped)
            return "ER#Service is stopped.";

        Date date0 = DateUtil.getCurDateTime();

        String response = execApiRequest(requestXml, ConfigData.OTA_HotelRatePlan_Url, "requestXML");

        int apiElapsedTime = DateUtil.getPastTime(date0);
        if (apiElapsedTime > 1000)
            logger.debug(ConfigData.OTA_HotelRatePlan_Request + " API: " + apiElapsedTime + "ms");

        //?
        try {
            SAXReader reader = new SAXReader();
            Document document = reader.read(new StringReader(response));
            Element rootElement = document.getRootElement();
            Element headerElement = rootElement.element("Header");
            if (headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
                //header?
                logger.debug("put headerElement to cache by key " + ConfigData.OTA_HotelRatePlan_Request);
                getCache().put(new net.sf.ehcache.Element(ConfigData.OTA_HotelRatePlan_Request, headerElement));
            } else {
                logger.error(response);
                logger.error("ResultCode is not Success.");
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        logger.debug("doSearchHotelRatePlan end..." + Thread.currentThread().getName());

        return response;
    }

    private RatePlanRequest createRatePlanRequest(String hotelCode, Date startDate, Date endDate) {
        RatePlanHotelRef ratePlanHotelRef = new RatePlanHotelRef();
        ratePlanHotelRef.setHotelCode(hotelCode);
        RatePlanCandidate ratePlanCandidate = new RatePlanCandidate();
        ratePlanCandidate.getRatePlanHotelRefs().add(ratePlanHotelRef);

        DateRange ratePlanDateRange = new DateRange();
        ratePlanDateRange.setStartDate(DateUtil.formatDate(startDate, "yyyy-MM-dd"));
        ratePlanDateRange.setEndDate(DateUtil.formatDate(endDate, "yyyy-MM-dd"));

        RatePlanRequest ratePlanRequest = new RatePlanRequest();
        ratePlanRequest.getHotelRatePlanCandidates().add(ratePlanCandidate);
        ratePlanRequest.setDateRange(ratePlanDateRange);

        return ratePlanRequest;
    }

    private String execSearchHotelRatePlan(List<HotelRatePlanRequestDto> requestDtos) {
        HotelRequestBody request = new HotelRequestBody();
        request.createHotelRatePlanRequest();

        String hotelCode;
        Date startDate;
        Date endDate;
        for (HotelRatePlanRequestDto dto : requestDtos) {
            hotelCode = dto.getHotelCode();
            startDate = dto.getStartDate();
            endDate = dto.getEndDate();

            RatePlanRequest ratePlanRequest = createRatePlanRequest(hotelCode, startDate, endDate);

            request.getHotelRatePlaneRequest().getRatePlanRequests().add(ratePlanRequest);
        }

        String xml;

        try {
            xml = createXml4HotelRequestBody(request, ConfigData.OTA_HotelRatePlan_Request);
        } catch (Exception e) {
            e.printStackTrace();
            return "ER#request xml is wrong.";
        }
        logger.debug(xml);
        String response = doSearchHotelRatePlan(xml);
        logger.debug(response);

        return response;
    }

    private String createXml4HotelRequestBody(HotelRequestBody request, String requestType) throws JAXBException {
        JAXBContext jc = JAXBContext.newInstance(HotelRequestBody.class);

        Marshaller m = jc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        m.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
        m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

        DocumentResult documentResult = new DocumentResult();
        m.marshal(request, documentResult);

        org.dom4j.Document document = documentResult.getDocument();
        org.dom4j.Element requestElement = document.getRootElement();

        org.dom4j.Document doc1 = DocumentHelper.createDocument();

        org.dom4j.Element rootElement = createRequestHeaderElement(doc1, requestType);
        rootElement.add(requestElement);

        return doc1.asXML();
    }

    private String execSearchHotelRatePlan(List<String> hotelCodes, Date startDate, Date endDate) {

        HotelRequestBody request = new HotelRequestBody();
        request.createHotelRatePlanRequest();

        for (String hotelCode : hotelCodes) {
            RatePlanRequest ratePlanRequest = createRatePlanRequest(hotelCode, startDate, endDate);
            request.getHotelRatePlaneRequest().getRatePlanRequests().add(ratePlanRequest);
        }

        String xml;

        try {
            xml = createXml4HotelRequestBody(request, ConfigData.OTA_HotelRatePlan_Request);
        } catch (Exception e) {
            e.printStackTrace();
            return "ER#request xml is wrong.";
        }
        logger.debug(xml);
        String response = doSearchHotelRatePlan(xml);
        logger.debug(response);

        return response;
    }

    @Override
    public String searchHotelRatePlan(List<String> hotelCodes, int periodId) {
        return searchHotelRatePlan(hotelCodes, periodId, false);
    }

    @Override
    public String importHotelRatePlanResponse(String filename) {
        String rs;
        SAXReader reader = new SAXReader();

        logger.info("filename: " + filename);

        File file = new File(filename);
        if (!file.exists())
            return null;

        Document document;// ?XML
        try {
            document = reader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
            return "error";
        }

        rs = processHotelRatePlanResponse(document, 1, new ArrayList<String>());

        return rs;
    }

    private synchronized String searchHotelCacheChange(int cityId, Date fromTimeStamp) {
        //headerAPI?
        Cache cache = getCache();
        String cacheKey = ConfigData.OTA_HotelCacheChange_Request;
        net.sf.ehcache.Element cacheElement = cache.get(cacheKey);
        if (cacheElement != null) {
            Element headerElement = (Element) cacheElement.getValue();
            int accessCount = Integer.parseInt(headerElement.attributeValue("AccessCount"));
            int currentCount = Integer.parseInt(headerElement.attributeValue("CurrentCount")) + 1;
            logger.info("AccessCount=" + headerElement.attributeValue("AccessCount") + ", CurrentCount="
                    + headerElement.attributeValue("CurrentCount") + ", ResetTime="
                    + headerElement.attributeValue("ResetTime"));
            if (currentCount >= accessCount) {
                try {
                    logger.info("Sleep for one minute.");
                    Thread.sleep(60000);
                } catch (InterruptedException ex) {
                    logger.warn(Thread.currentThread().getName() + " is interrupted.");
                    return "ER#SearchHotelCacheChange thread.sleep is interrupted.";
                }
            }
        }

        HotelRequestBody request = new HotelRequestBody();
        request.createHotelCacheChangeRequest();

        String cacheFromTimeStamp = DateUtil.formatDate(fromTimeStamp, "yyyy-MM-dd'T'HH:mm:ss");
        request.getHotelCacheChangeRequest().getCacheSearchCriteria().setCacheFromTimestamp(cacheFromTimeStamp);
        request.getHotelCacheChangeRequest().getCacheSearchCriteria().getCacheSearchCriterion()
                .setHotelCityCode(cityId);

        //logger.info("OTA_HotelCacheChange: " + cacheFromTimeStamp + ", " + cityId);

        String xml;

        try {
            xml = createXml4HotelRequestBody(request, ConfigData.OTA_HotelCacheChange_Request);
        } catch (JAXBException e) {
            e.printStackTrace();
            return "<xml>" + e.getMessage() + "</xml>";
        }

        String paraName = "requestXML";
        Date basetime = DateUtil.getCurDateTime();
        String response = execApiRequest(xml, ConfigData.OTA_HotelCacheChange_url, paraName);
        int spantime = DateUtil.getPastTime(basetime);
        logger.info(ConfigData.OTA_HotelCacheChange_Request + ": api elapsed  " + spantime + "ms");

        return response;
    }

    @Override
    public String importHotelCacheChangeResponse(String filename) {
        String rs;
        SAXReader reader = new SAXReader();

        logger.info("filename: " + filename);

        File file = new File(filename);
        if (!file.exists())
            return null;

        Document document;// ?XML
        try {
            document = reader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
            return "error";
        }

        rs = processHotelCacheChangeResponse(document);

        return rs;
    }

    /**
     * ?
     * ?7
     * 1. ?id
     * 2.
     * @return
     */
    @Override
    public String refreshAllHotelBasicInfo(String cityCode) {

        if (this.serviceStopped)
            return "ER#Service is stopped.";
        String rs = "";

        List<City> cities = cityDao.getAllCity();
        for (City city : cities) {
            if (this.serviceStopped)
                break;

            CacheCity cacheCity = hotelDao.getCacheCity(city.getOpenApiId());
            if (cacheCity != null) {
                //?7
                int spanDays = DateUtil.getPastDays(cacheCity.getCacheTime());
                if (spanDays < 7)
                    continue;
            }
            if (cityCode != null && !cityCode.equalsIgnoreCase("*")
                    && !cityCode.equalsIgnoreCase(city.getCityCode()))
                continue;

            String status = searchHotel(city.getOpenApiId());
            rs = status;
            if (status.substring(0, 2).equalsIgnoreCase("OK")) {
                if (cacheCity == null) {
                    cacheCity = new CacheCity();
                    cacheCity.setCityId(city.getOpenApiId());
                    cacheCity.setCreateTime(DateUtil.getCurDateTime());
                    cacheCity.setCacheTime(DateUtil.getCurDateTime());
                    hotelDao.createCacheCity(cacheCity);
                } else {
                    cacheCity.setCacheTime(DateUtil.getCurDateTime());
                    hotelDao.updateCacheCity(cacheCity);
                }
                //break;
            } else {
                //
                logger.warn("refreshAllHotelBasicInfoexit loop for (" + rs + ").");
                break;
            }
        }

        return rs;
    }

    /**
     * ?
     * 
     * @return
     */
    @Override
    public String refreshAllHotelDetail(String cityCode) {
        String rs = "HAHA";

        if (this.serviceStopped)
            return "ER#Service is stopped.";

        //hotelDetailDaoExecutor.execute(new HotelDetailDaoThread());

        List<String> hotelCodes0 = hotelDao.getAllHotelCodes2(cityCode);

        int count = 0;
        List<String> hotelCodes = new ArrayList<String>();
        int total = hotelCodes.size();
        int count0 = 0;
        for (String hotelCode : hotelCodes0) {
            count0++;
            if (this.serviceStopped)
                break;

            CacheHotel cacheHotel = hotelDao.getCacheHotel(hotelCode);
            if (cacheHotel != null) {
                //?7
                int spanDays = DateUtil.getPastDays(cacheHotel.getCacheTime());
                if (spanDays < 7) {
                    continue;
                }
            }

            hotelCodes.add(hotelCode);
            count++;
            if (count < hotelCountPerRequestHotelDetail && count0 < total)
                continue;

            String status = searchHotelDetail(hotelCodes, false);
            rs = status;
            if (status.substring(0, 2).equalsIgnoreCase("OK")) {
                count = 0;
                hotelCodes.clear();
            } else {
                //
                logger.warn("refreshAllHotelDetailexit loop for (" + rs + ").");
                break;
            }
        }

        return rs;
    }

    public int getMaxDaysOfHotelRate() {
        return maxDaysOfHotelRate;
    }

    public void setHotelCountPerRequestHotelDetail(int hotelCountPerRequestHotelDetail) {
        this.hotelCountPerRequestHotelDetail = hotelCountPerRequestHotelDetail;
    }

    private class HotelDetailDaoThread implements Runnable {

        //private volatile boolean stopRequested;
        private Thread runThread;

        @Override
        public void run() {
            runThread = Thread.currentThread();
            //stopRequested = false;
            logger.info(runThread.getName() + "(HotelDetailDaoThread) is running...");

            while (!hotelDetailQueue.isEmpty()) {
                if (serviceStopped)
                    break;

                try {
                    DocumentDto documentDto = hotelDetailQueue.take();
                    Document document = documentDto.getDocument();

                    processHotelDetailResponse(document);
                } catch (InterruptedException e) {
                    logger.warn(Thread.currentThread().getName() + " is interrupted.");
                    break;
                } catch (Exception ex) {
                    ex.printStackTrace();
                    logger.error(ex.getMessage());
                    break;
                }
            }

            logger.info(runThread.getName() + "(HotelDetailDaoThread) exit.");
        }

        //public void stopRequest() {
        //    stopRequested = true;
        //}
    }

    private class HotelRatePlanDaoThread implements Runnable {

        //private volatile boolean stopRequested;
        private Thread runThread;

        @Override
        public void run() {
            runThread = Thread.currentThread();
            //stopRequested = false;
            logger.debug(runThread.getName() + "(HotelRatePlanDaoThread) is running...");

            while (!ratePlanQueue.isEmpty()) {
                if (serviceStopped)
                    break;

                try {
                    DocumentDto documentDto = ratePlanQueue.poll(2, TimeUnit.SECONDS);
                    if (documentDto == null) {
                        Thread.sleep(50);
                        continue;
                    }
                    Document document = documentDto.getDocument();
                    int periodId = documentDto.getPeriodId();

                    logger.debug(runThread.getName() + " is running on " + periodId);
                    logger.debug("taken hotelCodes\'s size is " + documentDto.getHotelCodes().size());

                    executeSaveRatePlanDocument(document, periodId, documentDto.getHotelCodes());

                } catch (InterruptedException e) {
                    logger.warn(Thread.currentThread().getName() + " is interrupted.");
                    break;
                }

            }

            logger.debug(runThread.getName() + "(HotelRatePlanDaoThread) exit.");
        }

        //        public void stopRequest() {
        //            stopRequested = true;
        //        }
    }

    private class HotelCacheChangeDaoThread implements Runnable {

        //private volatile boolean stopRequested;
        private Thread runThread;

        @Override
        public void run() {
            runThread = Thread.currentThread();
            //stopRequested = false;

            logger.debug(runThread.getName() + "(HotelCacheChangeDaoThread) is running...");

            while (!hotelCacheChangeQueue.isEmpty()) {
                if (serviceStopped)
                    break;
                try {
                    DocumentDto documentDto = hotelCacheChangeQueue.poll(1, TimeUnit.SECONDS);
                    if (documentDto == null) {
                        Thread.sleep(50);
                        continue;
                    }

                    Document document = documentDto.getDocument();
                    executeSaveHotelCacheChangeDocument(document);
                } catch (InterruptedException x) {
                    logger.warn(Thread.currentThread().getName() + " is interrupted.");
                    break;
                } catch (Exception ex) {
                    logger.error(ex.getMessage());
                }
            }

            logger.debug(runThread.getName() + "(HotelCacheChangeDaoThread) exit.");
        }

        //        public void stopRequest() {
        //            stopRequested = true;
        //        }
    }

    /**
     * ?hotelCacheChange?
     */
    private class HotelCacheChangeDetailThread implements Runnable {

        private Thread runThread;

        @Override
        public void run() {
            runThread = Thread.currentThread();

            logger.debug(runThread.getName() + "(HotelCacheChangeDetailThread) is running...");

            //?hotelCacheChange?
            List<HotelRatePlanRequestDto> requestDtos = new ArrayList<HotelRatePlanRequestDto>();

            int count = ratePlanRequestQueue.drainTo(requestDtos, 10);

            if (serviceStopped)
                return;

            if (count > 0) {

                logger.debug("requestDtos\'s size is " + requestDtos.size());
                String response = execSearchHotelRatePlan(requestDtos);
                try {
                    SAXReader reader = new SAXReader();
                    Document document = reader.read(new StringReader(response));
                    if (document != null) {
                        Element rootElement = document.getRootElement();
                        Element headerElement = rootElement.element("Header");
                        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
                            logger.error("ER#ResultCode is not Success.");
                            logger.error(document.asXML());
                        } else {
                            List<String> hotelCodes = new ArrayList<String>();
                            for (HotelRatePlanRequestDto requestDto : requestDtos) {
                                hotelCodes.add(requestDto.getHotelCode());
                            }

                            processHotelRatePlanResponse(document, 0, hotelCodes);
                        }
                    }
                } catch (DocumentException e) {
                    logger.error(e.getMessage());
                }
            } else {
                logger.debug("drainTo is 0");
            }

            logger.debug(runThread.getName() + "(HotelCacheChangeDetailThread) exit.");
        }
    }

    private void executeSaveHotelCacheChangeDocument(Document document) {

        //?????
        Map uris = new HashMap();
        uris.put("ns", "http://www.opentravel.org/OTA/2003/05");
        XPath xpath = document.createXPath("//ns:OTA_HotelCacheChangeRS/*");
        xpath.setNamespaceURIs(uris);
        List myNodes = xpath.selectNodes(document);

        List<CacheHotelCacheChange> hotelCacheChanges = new ArrayList<CacheHotelCacheChange>();

        XStream xs = createXStream();
        xs.alias("CacheChangeInfo", HotelCacheChangeInfo.class);
        xs.processAnnotations(HotelCacheChangeInfo.class);

        for (Iterator it = myNodes.iterator(); it.hasNext();) {
            Element element = (Element) it.next();

            if (!element.getName().equalsIgnoreCase("CacheChangeInfo")) {
                continue;
            }

            //String xml = element.asXML();
            HotelCacheChangeInfo hotelCacheChangeInfo = (HotelCacheChangeInfo) xs.fromXML(element.asXML());

            CacheHotelCacheChange cacheHotelCacheChange = new CacheHotelCacheChange();
            cacheHotelCacheChange.setHotelCode(hotelCacheChangeInfo.getHotelCode());
            cacheHotelCacheChange.setRatePlanCode(hotelCacheChangeInfo.getOtherInfo().getRatePlanCode());
            try {
                cacheHotelCacheChange
                        .setStartDate(DateUtil.convertToDate(hotelCacheChangeInfo.getDateRange().getStartDate()));
                cacheHotelCacheChange
                        .setEndDate(DateUtil.convertToDate(hotelCacheChangeInfo.getDateRange().getEndDate()));
            } catch (Exception ex) {
                logger.error(ex.getMessage());
                continue;
            }

            hotelCacheChanges.add(cacheHotelCacheChange);

        } //end for
        hotelDao.createCacheHotelCacheChanges(hotelCacheChanges);
    }

    @Override
    public String refreshAllRatePlan(String cityCode, int ratePlanStatus) {
        String rs = "";
        int count = 0;

        if (this.serviceStopped)
            return "ER#Service is stopped.";

        //hotelRatePlanDaoExecutor.execute(new HotelRatePlanDaoThread());

        cn.buk.hotel.entity.HotelInfo hotelInfo;
        boolean bBreak = false;
        for (int periodId = 1; periodId <= 4; periodId++) {
            if (this.serviceStopped)
                break;

            int minDay = (periodId - 1) * 28;
            if (minDay >= this.maxDaysOfHotelRate)
                continue;

            List<String> hotelCodes1 = new ArrayList<String>();
            Date baseTime = DateUtil.getCurDateTime();
            List<String> hotelCodes = null;
            if (ratePlanStatus == 0)
                hotelCodes = hotelDao.getHotelCodes4RatePlan(periodId, cityCode);
            else
                hotelCodes = hotelDao.getHotelCodes4RatePlan(periodId, cityCode, ratePlanStatus);
            hotelCodes = hotelCodes != null ? hotelCodes : new ArrayList<String>();

            int span = DateUtil.getPastMinutes(baseTime);
            if (span >= 5) {
                logger.error("hotelDao.getHotelCodes4RatePlan(periodId)  " + span + ", ?");
                break;
            }

            int total = hotelCodes.size();
            int progress = 0;
            for (String hotelCode : hotelCodes) {
                if (this.serviceStopped)
                    break;
                progress++;

                //header?
                //timeToLiveSecond is the time in seconds from the point of creation in the cache till it expires (regardless of how frequently it is accessed in that time)
                //timeToIdleSeconds is the time in seconds before an object in the cache expires if it is not accessed.
                //getCache().put(new net.sf.ehcache.Element(ConfigData.HOTEL_CODES_WAITING_FOR_RATE_PLAN, copyHotelCodes, false, 1200000, 1800000));

                hotelInfo = hotelDao.getHotelInfoByHotelCode(hotelCode);
                if (hotelInfo == null || (hotelInfo.getRatePlanStatus() == -1 && ratePlanStatus == 0)) {
                    logger.debug(hotelCode + " is null or rate plan status is -1.");
                    continue;
                }

                CacheRatePlan cacheRatePlan = hotelDao.getCacheRatePlan(hotelCode, periodId);
                if (cacheRatePlan != null) {
                    //?7

                    int spanDays;
                    if (cacheRatePlan.getBeginTime() != null)
                        spanDays = DateUtil.getPastDays(cacheRatePlan.getBeginTime());
                    else if (cacheRatePlan.getEndTime() != null)
                        spanDays = DateUtil.getPastDays(DateUtil.getOnlyDate(cacheRatePlan.getEndTime()));
                    else
                        spanDays = 30;

                    if (spanDays < 7 && progress < total)
                        continue;
                } else {
                    cacheRatePlan = new CacheRatePlan();
                    cacheRatePlan.setHotelCode(hotelCode);
                    cacheRatePlan.setPeriodId(periodId);
                    cacheRatePlan.setBeginTime(DateUtil.getCurDateTime());
                    hotelDao.createCacheRatePlan(cacheRatePlan);
                }

                hotelCodes1.add(hotelCode);
                count++;
                if (count < 10 && progress < total)
                    continue;

                rs = searchHotelRatePlan(hotelCodes1, periodId);
                if (rs.substring(0, 2).equalsIgnoreCase("OK")) {
                    count = 0;
                    hotelCodes1.clear();
                } else {
                    logger.error("exit loop for (" + rs + ").");
                    bBreak = true;
                    break;
                }
                logger.info("refreshAllRatePlan\'s progress: " + Integer.toString(progress) + "/"
                        + Integer.toString(total) + ", " + Integer.toString(periodId));
            }
            if (bBreak)
                break;
        }

        return rs;
    }

    /**
     * ?;
     * ?
     * cityCoded ??;???.
     * @param cityCode
     * @return
     */
    @Override
    public String refreshAllHotelCacheChange(String cityCode) {

        //hotelCacheChangeDaoExecutor.execute( new HotelCacheChangeDaoThread());
        if (this.serviceStopped)
            return "ER#Service is stopped.";

        List<City> cities = cityDao.getCityHotelGreaterThan100();
        int max = cities.size();
        int index = 0;

        Date timestamp1 = DateUtil.getCurDateTime();
        timestamp1 = DateUtil.addMinutes(timestamp1, -intervalCacheChange);
        timestamp1 = DateUtil.getDateOnMinute(timestamp1);
        String cacheFromTimeStamp = DateUtil.formatDate(timestamp1, "yyyy-MM-dd'T'HH:mm:ss");
        for (City city : cities) {
            if (this.serviceStopped)
                break;
            if (city.getOpenApiId() == 0)
                continue;
            if (!city.getCountryCode().equalsIgnoreCase("CN"))
                continue;
            if (cityCode != null && !cityCode.equalsIgnoreCase("*")
                    && !cityCode.equalsIgnoreCase(city.getCityCode()))
                continue;

            index++;
            logger.debug("refreshHotelCacheChange\'s process: " + index + "/" + max);
            logger.info("OTA_HotelCacheChange: " + cacheFromTimeStamp + ", " + city.getOpenApiId() + ", " + index);

            String xml = searchHotelCacheChange(city.getOpenApiId(), timestamp1);
            if (xml.length() == 0)
                break;
            processHotelCacheChange(xml);
        }

        return "OK";
    }

    @Override
    public String calculateSignature(String timestamp, String requestType) {
        try {
            return SignatureUtils.CalculationSignature(timestamp, this.allianceId, this.secretKey, this.sid,
                    requestType);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    private class HotelRatePlanRequestDto {
        private String hotelCode;
        private Date startDate;
        private Date endDate;

        public String getHotelCode() {
            return hotelCode;
        }

        public void setHotelCode(String hotelCode) {
            this.hotelCode = hotelCode;
        }

        public Date getStartDate() {
            return startDate;
        }

        public void setStartDate(Date startDate) {
            this.startDate = startDate;
        }

        public Date getEndDate() {
            return endDate;
        }

        public void setEndDate(Date endDate) {
            this.endDate = endDate;
        }
    }

    @Override
    public void retrieveHotelCacheChangeDetail() {
        if (this.serviceStopped)
            return;
        String hotelCode0 = null;
        Date startDate0 = null, endDate0 = null;
        List<CacheHotelCacheChange> cacheChanges = hotelDao.getHotelCacheChanges();

        int total = cacheChanges.size();
        int count = 0;
        for (CacheHotelCacheChange cacheHotelCacheChange : cacheChanges) {
            if (this.serviceStopped)
                return;

            String hotelCode = cacheHotelCacheChange.getHotelCode();
            Date startDate = cacheHotelCacheChange.getStartDate();
            Date endDate = cacheHotelCacheChange.getEndDate();

            count++;

            //????
            if (hotelCode0 != null && hotelCode0.equalsIgnoreCase(hotelCode)
                    && startDate0.getTime() == startDate.getTime() && endDate0.getTime() == endDate.getTime()) {
                logger.debug("HotelCacheChange\'s hotelCode is " + hotelCode + ", start is "
                        + DateUtil.formatDate(startDate0, "yyyy-MM-dd") + ", end is "
                        + DateUtil.formatDate(endDate0, "yyyy-MM-dd") + ", ignored.");
                continue;
            }
            hotelCode0 = hotelCode;
            startDate0 = startDate;
            endDate0 = endDate;

            int spans = DateUtil.getPastDays(startDate, DateUtil.getCurDate());
            if (spans > 90) {
                logger.debug("HotelCacheChange\'s start date is " + DateUtil.formatDate(startDate, "yyyy-MM-dd")
                        + ", " + Integer.toString(spans) + " > 90 days, ignored.");
                continue;
            }
            spans = DateUtil.getPastDays(endDate, DateUtil.getCurDate());
            if (spans > 90)
                endDate = DateUtil.add(DateUtil.getCurDate(), 90);

            spans = DateUtil.getPastDays(endDate, startDate);
            if (spans > 28) {
                logger.warn("HotelCacheChange\'s start is " + DateUtil.formatDate(startDate, "yyyy-MM-dd")
                        + ", end is " + DateUtil.formatDate(endDate, "yyyy-MM-dd") + ", split it.");
                for (int i = 1; i <= 5; i++) {
                    Date startDate1 = DateUtil.add(startDate, (i - 1) * 28);
                    Date endDate1 = DateUtil.add(startDate, i * 28);

                    boolean b = DateUtil.getPastDays(endDate1, endDate) > 0;
                    if (b)
                        endDate1 = endDate;

                    HotelRatePlanRequestDto dto = new HotelRatePlanRequestDto();
                    dto.setHotelCode(hotelCode);
                    dto.setStartDate(startDate1);
                    dto.setEndDate(endDate1);

                    logger.warn("HotelCacheChange\'s hotelCode is " + hotelCode + ", start is "
                            + DateUtil.formatDate(startDate1, "yyyy-MM-dd") + ", end is "
                            + DateUtil.formatDate(endDate1, "yyyy-MM-dd") + ".");

                    try {
                        ratePlanRequestQueue.put(dto);
                    } catch (InterruptedException e) {
                        logger.warn(Thread.currentThread().getName() + " is interrupted.");
                        return;
                    }
                    if (b)
                        break;
                }
            } else {
                HotelRatePlanRequestDto dto = new HotelRatePlanRequestDto();
                dto.setHotelCode(hotelCode);
                dto.setStartDate(startDate);
                dto.setEndDate(endDate);

                try {
                    ratePlanRequestQueue.put(dto);
                } catch (InterruptedException e) {
                    logger.warn(Thread.currentThread().getName() + " is interrupted.");
                    return;
                }
            }

            //if (count % 10 == 0 || count >= total)
            hotelCacheChangeDetailExecutor.execute(new HotelCacheChangeDetailThread());

            if (count % 100 == 0)
                logger.info("retrieveHotelCacheChangeDetail\'s progress: " + Integer.toString(count) + "/"
                        + Integer.toString(total));
        }
    }

    @Override
    public void stopService() {
        this.serviceStopped = true;

        hotelDetailDaoExecutor.shutdown();
        try {
            hotelDetailDaoExecutor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hotelCacheChangeDaoExecutor.shutdown();
        try {
            hotelCacheChangeDaoExecutor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hotelRatePlanDaoExecutor.shutdown();
        try {
            hotelRatePlanDaoExecutor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        hotelCacheChangeDetailExecutor.shutdown();
        try {
            hotelCacheChangeDetailExecutor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        cacheManager.shutdown();
    }

    /**
     * ??
     * @param hotelCode
     * @param checkInDate
     * @param checkOutDate
     * @param ratePlanCode
     * @param roomQuantity
     * @param guestCount
     * @param lateArrivalTime
     * @return
     */
    @Override
    public String checkHotelAvail(String hotelCode, Date checkInDate, Date checkOutDate, String ratePlanCode,
            int roomQuantity, int guestCount, Date lateArrivalTime, boolean returnXml) {
        HotelRequestBody request = new HotelRequestBody();

        //
        if (checkInDate.getTime() < DateUtil.getCurDate().getTime())
            checkInDate = DateUtil.getCurDate();
        if (checkInDate.getTime() >= checkOutDate.getTime())
            checkOutDate = DateUtil.add(checkInDate, 1);
        if (lateArrivalTime.getTime() <= DateUtil.getCurDateTime().getTime())
            lateArrivalTime = DateUtil.addMinutes(DateUtil.getCurDateTime(), 5);

        request.createHotelAvailRequest();
        HotelAvailRequestSegment hotelAvailRequestSegment = new HotelAvailRequestSegment();
        request.getHotelAvailRequest().getHotelAvailRequestSegments().add(hotelAvailRequestSegment);

        HotelSearchCriterion criterion = hotelAvailRequestSegment.getHotelSearchCriteria().getCriterion();
        criterion.getHotelRef().setHotelCode(hotelCode);
        criterion.getHotelStayDateRange().setStartDate(DateUtil.formatDate(checkInDate, "yyyy-MM-dd"));
        criterion.getHotelStayDateRange().setEndDate(DateUtil.formatDate(checkOutDate, "yyyy-MM-dd"));

        HotelRatePlanCandidate ratePlanCandidate = new HotelRatePlanCandidate();
        criterion.getRatePlanCandidates().add(ratePlanCandidate);
        ratePlanCandidate.setRatePlanCode(ratePlanCode);

        RoomStayCandidate roomStayCandidate = new RoomStayCandidate();
        criterion.getRoomStayCandidates().add(roomStayCandidate);
        roomStayCandidate.setQuantity(roomQuantity);

        roomStayCandidate.getRoomGuestCount().getGuestCount().setCount(guestCount);
        roomStayCandidate.getRoomGuestCount().setPerRoom(false);

        criterion.getHotelLateArrivalTime()
                .setLateArrivalTime(DateUtil.formatDate(lateArrivalTime, "yyyy-MM-dd'T'HH:mm:ss"));

        String xml;

        try {
            xml = createXml4HotelRequestBody(request, ConfigData.OTA_HotelAvail_Request);
        } catch (JAXBException ex) {
            ex.printStackTrace();
            logger.error(ex.getMessage());
            return "ER";
        }

        logger.debug(xml);

        //        return xml;

        if (serviceStopped)
            return "ER#Service stopped.";
        String response = execApiRequest(xml, ConfigData.OTA_HotelAvail_Url, "requestXML");

        logger.debug(response);

        if (returnXml)
            return response;

        //?
        SAXReader reader = new SAXReader();

        Document document;// ?XML
        try {
            document = reader.read(new StringReader(response));

            if (serviceStopped)
                return "ER#Service stopped.";
            response = processHotelAvailResponse(document);
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            return "ER";
        }

        return response;

    }

    @Override
    public HotelAvailResult checkHotelAvail(String hotelCode, Date checkInDate, Date checkOutDate,
            String ratePlanCode, int roomQuantity, int guestCount, Date lateArrivalTime) {
        String responseXml = checkHotelAvail(hotelCode, checkInDate, checkOutDate, ratePlanCode, roomQuantity,
                guestCount, lateArrivalTime, true);
        List<HotelAvailResponseRoomStay> roomStays = processHotelAvailResponse(responseXml);

        HotelAvailResult hotelAvailResult = new HotelAvailResult();
        if (roomStays.size() > 0) {
            if (roomStays.get(0).isAvailable())
                hotelAvailResult.setStatus("OK");
            else
                hotelAvailResult.setStatus("ER");
        }

        return hotelAvailResult;
    }

    @Override
    public String importHotelAvailResponse(String xml_file) {
        String rs;
        SAXReader reader = new SAXReader();

        logger.info("filename: " + xml_file);

        File file = new File(xml_file);
        if (!file.exists())
            return null;

        Document document;// ?XML
        try {
            document = reader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
            return "error";
        }

        rs = processHotelAvailResponse(document);

        return rs;
    }

    private List<HotelAvailResponseRoomStay> processHotelAvailResponse(String responseXml) {
        List<HotelAvailResponseRoomStay> roomStays = new ArrayList<HotelAvailResponseRoomStay>();

        //?
        SAXReader reader = new SAXReader();
        Document document;// ?XML
        try {
            document = reader.read(new StringReader(responseXml));

            //?????
            Map uris = new HashMap();
            uris.put("ns", "http://www.opentravel.org/OTA/2003/05");
            XPath xpath = document.createXPath("//ns:RoomStays/*");
            xpath.setNamespaceURIs(uris);
            List myNodes = xpath.selectNodes(document);

            if (myNodes.size() == 0) {
                logger.warn("ER#There is nothing.");
                return roomStays;
            }

            XStream xs = createXStream();
            xs.alias("RoomStay", HotelAvailResponseRoomStay.class);
            xs.processAnnotations(HotelAvailResponseRoomStay.class);

            String xml;

            for (Iterator it = myNodes.iterator(); it.hasNext();) {
                //if (serviceStopped) break;

                Element element = (Element) it.next();

                xml = element.asXML();
                HotelAvailResponseRoomStay roomStay = (HotelAvailResponseRoomStay) xs.fromXML(xml);
                roomStays.add(roomStay);
            }

        } catch (Exception ex) {
            logger.error(ex.getMessage());
        }

        return roomStays;
    }

    private String processHotelAvailResponse(Document document) {
        if (document == null)
            return "ER#Document is null.";

        //?????
        Map uris = new HashMap();
        uris.put("ns", "http://www.opentravel.org/OTA/2003/05");
        XPath xpath = document.createXPath("//ns:RoomStays/*");
        xpath.setNamespaceURIs(uris);
        List myNodes = xpath.selectNodes(document);

        String rs = "HAHA";

        if (myNodes.size() == 0) {
            return "ER#There is nothing.";
        }

        logger.debug(Thread.currentThread().getName() + " is on processHotelAvailResponse.");

        XStream xs = createXStream();
        xs.alias("RoomStay", HotelAvailResponseRoomStay.class);
        xs.processAnnotations(HotelAvailResponseRoomStay.class);

        String xml;

        for (Iterator it = myNodes.iterator(); it.hasNext();) {
            //if (serviceStopped) break;

            Element element = (Element) it.next();

            xml = element.asXML();
            HotelAvailResponseRoomStay response = (HotelAvailResponseRoomStay) xs.fromXML(xml);

            if (response.isAvailable())
                System.out.println("available for booking");
            else
                System.out.println("closed");

        } //end for

        return rs;
    }

    private String processHotelCacheChange(String xml) {
        String retval;
        //?
        SAXReader reader = new SAXReader();

        Document document;// ?XML
        try {
            document = reader.read(new StringReader(xml));
            retval = processHotelCacheChangeResponse(document);
        } catch (Exception ex) {
            ex.printStackTrace();
            return "ER";
        }

        return retval;
    }

    public void setCityDao(CityDao cityDao) {
        this.cityDao = cityDao;
    }

    public void setHotelDao(HotelDao hotelDao) {
        this.hotelDao = hotelDao;
    }

}