UADetectorServiceFactory.java

/*******************************************************************************
 * Copyright 2012 André Rouél
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package net.sf.uadetector.service;

import java.net.URL;

import net.sf.uadetector.UserAgentStringParser;
import net.sf.uadetector.datareader.DataReader;
import net.sf.uadetector.datareader.XmlDataReader;
import net.sf.uadetector.datastore.AbstractDataStore;
import net.sf.uadetector.datastore.CachingXmlDataStore;
import net.sf.uadetector.datastore.OnlineXmlDataStore;
import net.sf.uadetector.parser.UpdatingUserAgentStringParserImpl;
import net.sf.uadetector.parser.UserAgentStringParserImpl;

/**
 * Service factory to get preconfigured instances of {@code UserAgentStringParser} implementations.
 * 
 * @author André Rouél
 */
public final class UADetectorServiceFactory {

	/**
	 * Holder to load the parser only when it's needed.
	 */
	private static final class CachingAndUpdatingParserHolder {
		private static final UserAgentStringParser INSTANCE = new UpdatingUserAgentStringParserImpl(
				CachingXmlDataStore.createCachingXmlDataStore(RESOURCE_MODULE));
	}

	/**
	 * Holder to load the parser only when it's needed.
	 */
	private static final class OnlineUpdatingParserHolder {
		private static final UserAgentStringParser INSTANCE = new UpdatingUserAgentStringParserImpl(new OnlineXmlDataStore(RESOURCE_MODULE));
	}

	/**
	 * A simple implementation to store <em>UAS data</em> delivered in this module (called <em>uadetector-resource</em>)
	 * only in the heap space.
	 * 
	 * @author André Rouél
	 */
	public static final class ResourceModuleXmlDataStore extends AbstractDataStore {

		/**
		 * The default data reader to read in <em>UAS data</em> in XML format
		 */
		private static final DataReader DEFAULT_DATA_READER = new XmlDataReader();

		/**
		 * Path where the UAS data file is stored for the {@code ClassLoader}
		 */
		private static final String PATH = "net/sf/uadetector/resources";

		/**
		 * {@link URL} to the UAS data delivered in this module
		 */
		public static final URL UAS_DATA = ResourceModuleXmlDataStore.class.getClassLoader().getResource(PATH + "/uas.xml");

		/**
		 * {@link URL} to the version information of the delivered UAS data in this module
		 */
		public static final URL UAS_VERSION = ResourceModuleXmlDataStore.class.getClassLoader().getResource(PATH + "/uas.version");

		/**
		 * Constructs an {@code ResourceModuleXmlDataStore} by reading <em>UAS data</em> from the specified URL
		 * {@link #UAS_DATA} (in XML format).
		 */
		public ResourceModuleXmlDataStore() {
			super(DEFAULT_DATA_READER, UAS_DATA, UAS_VERSION, DEFAULT_CHARSET);
		}

	}

	/**
	 * Data store filled with the <em>UAS data</em> that are shipped with this module (JAR)
	 */
	public static final ResourceModuleXmlDataStore RESOURCE_MODULE = new ResourceModuleXmlDataStore();

	/**
	 * {@link UserAgentStringParser} filled with the <em>UAS data</em> that are shipped with this module (JAR)
	 */
	public static final UserAgentStringParser RESOURCE_MODULE_PARSER = new UserAgentStringParserImpl<ResourceModuleXmlDataStore>(
			RESOURCE_MODULE);

	/**
	 * Returns an implementation of {@link UserAgentStringParser} which checks at regular intervals for new versions of
	 * <em>UAS data</em> (also known as database). When newer data available, it automatically loads and updates it.
	 * Additionally the loaded data are stored in a cache file.
	 * 
	 * <p>
	 * At initialization time the returned parser will be loaded with the <em>UAS data</em> of the cache file. If the
	 * cache file doesn't exist or is empty the data of this module will be loaded. The initialization is started only
	 * when this method is called the first time.
	 * 
	 * <p>
	 * The update of the data store runs as background task. With this feature we try to reduce the initialization time
	 * of this <code>UserAgentStringParser</code>, because a network connection is involved and the remote system can be
	 * not available or slow.
	 * 
	 * <p>
	 * The static class definition {@code CachingAndUpdatingParserHolder} within this factory class is <em>not</em>
	 * initialized until the JVM determines that {@code CachingAndUpdatingParserHolder} must be executed. The static
	 * class {@code CachingAndUpdatingParserHolder} is only executed when the static method
	 * {@code getOnlineUserAgentStringParser} is invoked on the class {@code UADetectorServiceFactory}, and the first
	 * time this happens the JVM will load and initialize the {@code CachingAndUpdatingParserHolder} class.
	 * 
	 * <p>
	 * If during the operation the Internet connection gets lost, then this instance continues to work properly (and
	 * under correct log level settings you will get an corresponding log messages).
	 * 
	 * @return an user agent string parser with updating service
	 */
	public static UserAgentStringParser getCachingAndUpdatingParser() {
		return CachingAndUpdatingParserHolder.INSTANCE;
	}

	/**
	 * Returns an implementation of {@link UserAgentStringParser} which checks at regular intervals for new versions of
	 * <em>UAS data</em> (also known as database). When newer data available, it automatically loads and updates it.
	 * 
	 * <p>
	 * At initialization time the returned parser will be loaded with the <em>UAS data</em> of this module (the shipped
	 * one within the <em>uadetector-resources</em> JAR) and tries to update it. The initialization is started only when
	 * this method is called the first time.
	 * 
	 * <p>
	 * The update of the data store runs as background task. With this feature we try to reduce the initialization time
	 * of this <code>UserAgentStringParser</code>, because a network connection is involved and the remote system can be
	 * not available or slow.
	 * 
	 * <p>
	 * The static class definition {@code OnlineUpdatingParserHolder} within this factory class is <em>not</em>
	 * initialized until the JVM determines that {@code OnlineUpdatingParserHolder} must be executed. The static class
	 * {@code OnlineUpdatingParserHolder} is only executed when the static method {@code getOnlineUserAgentStringParser}
	 * is invoked on the class {@code UADetectorServiceFactory}, and the first time this happens the JVM will load and
	 * initialize the {@code OnlineUpdatingParserHolder} class.
	 * 
	 * <p>
	 * If during the operation the Internet connection gets lost, then this instance continues to work properly (and
	 * under correct log level settings you will get an corresponding log messages).
	 * 
	 * @return an user agent string parser with updating service
	 */
	public static UserAgentStringParser getOnlineUpdatingParser() {
		return OnlineUpdatingParserHolder.INSTANCE;
	}

	/**
	 * Returns an implementation of {@link UserAgentStringParser} with no updating functions. It will be loaded by using
	 * the shipped <em>UAS data</em> (also known as database) of this module. The database is loaded once during
	 * initialization. The initialization is started at class loading of this class ({@code UADetectorServiceFactory}).
	 * 
	 * @return an user agent string parser without updating service
	 */
	public static UserAgentStringParser getResourceModuleParser() {
		return RESOURCE_MODULE_PARSER;
	}

	private UADetectorServiceFactory() {
		// This class is not intended to create objects from it.
	}

}