nz.net.catalyst.mobile.dds.WurflCapabilityServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for nz.net.catalyst.mobile.dds.WurflCapabilityServiceImpl.java

Source

/*
 * Copyright (C) 2010  Catalyst IT Limited
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package nz.net.catalyst.mobile.dds;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.inject.Inject;
import javax.servlet.ServletContext;

import org.apache.commons.jci.listeners.FileChangeListener;
import org.apache.commons.jci.monitor.FilesystemAlterationMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sourceforge.wurfl.core.CapabilityNotDefinedException;
import net.sourceforge.wurfl.core.CustomWURFLHolder;
import net.sourceforge.wurfl.core.Device;
import net.sourceforge.wurfl.core.WURFLHolder;

/**
 * Capability service implementation that uses the new wurfl java api
 * This also wathces the wurfl files and reloads them when changed
 * 
 * @author jun yamog
 *
 */

public class WurflCapabilityServiceImpl implements CapabilityService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final static String WURFL_FILENAME = "wurfl.xml";

    // the variables below is declared as volatile to guarentee read consistency
    // across threads, writing is done inside a ReentrantLock
    // we only do the write on a single thread, see reloadWurfl method
    private volatile WURFLHolder wurflHolder;
    private volatile StatusInfo statusInfo;
    private Lock wurflReloadLock = new ReentrantLock();

    @Inject
    private ServletContext servletContext;

    private List<FilesystemAlterationMonitor> famList = new ArrayList<FilesystemAlterationMonitor>();

    private String wurflDirPath;
    private File wurflFile;
    private File[] wurflPatchFiles;

    /**
     * init will properly initialize this service.  this would look for wurfl files
     * and instantiate a wurfl holder
     */
    public void init() {

        // before running init check to see if attributes has been injected/set
        if (wurflDirPath == null)
            throw new IllegalStateException("wurflDirPath not properly set");

        // look for wurfl file and patches
        File wurflDir = new File(wurflDirPath);
        // if file does not exists, it might be relative to the servlet
        if (!wurflDir.exists() && servletContext != null)
            wurflDir = new File(servletContext.getRealPath(wurflDirPath));
        // check again
        if (!wurflDir.exists())
            throw new IllegalArgumentException("wurflDirPath " + wurflDir.getAbsolutePath() + " does not exists");

        if (!wurflDir.isDirectory())
            throw new IllegalArgumentException(
                    "wurflDirPath " + wurflDir.getAbsolutePath() + " is not a directory");

        ArrayList<File> patchFiles = new ArrayList<File>();

        logger.info("search for wurfl file and patches on: " + wurflDir.getAbsolutePath());
        for (String filePath : wurflDir.list()) {
            File file = new File(wurflDir.getAbsoluteFile() + "/" + filePath);
            if (WURFL_FILENAME.equals(file.getName())) {
                wurflFile = file;
                logger.debug("wurfl file: " + wurflFile.getAbsolutePath());
            } else if (file.getName().endsWith(".xml")) {
                patchFiles.add(file);
                logger.debug("wurfl patch file: " + file.getAbsolutePath());
            }

        }
        this.wurflPatchFiles = patchFiles.toArray(new File[] {});

        // initialize wurfl holder
        this.wurflHolder = new CustomWURFLHolder(this.wurflFile, this.wurflPatchFiles);

        startWatchingFiles();
        this.statusInfo = getNewStatusInfo("");
    }

    public void cleanup() {
        logger.info("stopping watching wurfl");
        stopWatchingFiles();
    }

    @Override
    public Map<String, Object> getCapabilitiesForDevice(RequestInfo requestInfo, List<String> capabilities) {
        Device device = wurflHolder.getWURFLManager().getDeviceForRequest(requestInfo.getUserAgent());
        Map<String, Object> capabilitiesMap = new HashMap<String, Object>();

        for (String capability : capabilities) {
            String capabilityStr;

            try {
                if ("device_id".equals(capability))
                    capabilityStr = device.getId();
                else
                    capabilityStr = device.getCapability(capability);
            } catch (CapabilityNotDefinedException e) {
                logger.warn("capability = " + capability + " does not exists");
                continue;
            }

            try {
                Integer capabilityInt = Integer.parseInt(capabilityStr);
                capabilitiesMap.put(capability, capabilityInt);
            } catch (NumberFormatException e) {
                if ("true".equalsIgnoreCase(capabilityStr) || "false".equalsIgnoreCase(capabilityStr)) {
                    Boolean capabilityBoolean = Boolean.parseBoolean(capabilityStr);
                    capabilitiesMap.put(capability, capabilityBoolean);
                } else {
                    capabilitiesMap.put(capability, capabilityStr);
                }
            }
        }

        return capabilitiesMap;
    }

    @Override
    public StatusInfo getStatusInfo() {
        return this.statusInfo;
    }

    protected StatusInfo getNewStatusInfo(String wurflError) {
        // look for the last modified date of files
        long lastModified = wurflFile.lastModified();

        for (File patchFile : wurflPatchFiles) {
            long wurflPatchLastModified = patchFile.lastModified();
            if (lastModified < wurflPatchLastModified)
                lastModified = wurflPatchLastModified;
        }

        StatusInfo statusInfo = new StatusInfo(lastModified, wurflError);
        return statusInfo;

    }

    /**
     * create listeners and monitors for wurfl and its patches
     */
    protected synchronized void startWatchingFiles() {
        famList.clear();

        FilesystemAlterationMonitor fam = new FilesystemAlterationMonitor();
        FileChangeListener wurflListener = new WurflFileListener();
        fam.addListener(wurflFile, wurflListener);
        fam.start();
        famList.add(fam);
        logger.debug("watching " + wurflFile.getAbsolutePath());

        for (File patchFile : wurflPatchFiles) {
            fam = new FilesystemAlterationMonitor();
            FileChangeListener wurflPatchListener = new WurflFileListener();
            fam.addListener(patchFile, wurflPatchListener);
            fam.start();
            famList.add(fam);
            logger.debug("watching " + patchFile.getAbsolutePath());
        }

    }

    protected synchronized void stopWatchingFiles() {
        for (FilesystemAlterationMonitor fam : famList)
            fam.stop();
    }

    protected void reloadWurfl() {
        if (!wurflReloadLock.tryLock()) {
            logger.warn(
                    "unable to obtain wurflReloadLock another thread be in the process of reloading, not reloading");
            return;
        }

        try {
            WURFLHolder tempWurflHolder = new CustomWURFLHolder(this.wurflFile, this.wurflPatchFiles);
            wurflHolder = tempWurflHolder;
            this.statusInfo = getNewStatusInfo("");
            logger.info("reload successful");
        } catch (Exception e) {
            logger.error("error in reloading wurfl", e);
            String errorMessage = "Unknown error";
            if (e.getCause() != null && e.getCause().getMessage() != null)
                errorMessage = e.getCause().getMessage();
            else if (e.getMessage() != null)
                errorMessage = e.getMessage();
            this.statusInfo = getNewStatusInfo(errorMessage);
        } finally {
            wurflReloadLock.unlock();
        }
    }

    public void setWurflDirPath(String wurflDirPath) {
        this.wurflDirPath = wurflDirPath;
    }

    protected class WurflFileListener extends FileChangeListener {

        public void onFileChange(File pFile) {
            super.onFileChange(pFile);
            if (hasChanged()) {
                logger.info(pFile.getAbsolutePath() + " file changed, reloading");
                reloadWurfl();
            }
        }
    }

}