All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.restlet.data.ClientInfo Maven / Gradle / Ivy

Go to download

This OSGi bundle wraps org.restlet, and com.noelios.restlet ${pkgVersion} jar files.

The newest version!
/**
 * Copyright 2005-2008 Noelios Technologies.
 * 
 * The contents of this file are subject to the terms of the following open
 * source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 (the "Licenses"). You can
 * select the license that you prefer but you may not use this file except in
 * compliance with one of these Licenses.
 * 
 * You can obtain a copy of the LGPL 3.0 license at
 * http://www.gnu.org/licenses/lgpl-3.0.html
 * 
 * You can obtain a copy of the LGPL 2.1 license at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 * 
 * You can obtain a copy of the CDDL 1.0 license at
 * http://www.sun.com/cddl/cddl.html
 * 
 * See the Licenses for the specific language governing permissions and
 * limitations under the Licenses.
 * 
 * Alternatively, you can obtain a royaltee free commercial license with less
 * limitations, transferable or non-transferable, directly at
 * http://www.noelios.com/products/restlet-engine
 * 
 * Restlet is a registered trademark of Noelios Technologies.
 */

package org.restlet.data;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.restlet.resource.Resource;
import org.restlet.resource.Variant;
import org.restlet.util.Engine;
import org.restlet.util.Template;
import org.restlet.util.Variable;

/**
 * Client specific data related to a call. When extracted from a request, most
 * of these data are directly taken from the underlying headers. There are some
 * exceptions: agentAttributes and mainAgentProduct which are taken from the
 * agent name (for example the "user-agent" header for HTTP requests).
*
* As described by the HTTP specification, the "user-agent" can be seen as a * ordered list of products name (ie a name and a version) and/or comments.
*
* Each HTTP client (mainly browsers and web crawlers) defines its own * "user-agent" header which can be seen as the "signature" of the client. * Unfortunately, there is no rule to identify clearly a kind a client and its * version (let's say firefox 2.x, Internet Explorer IE 7.0, Opera, etc) * according to its signature. Each signature follow its own rules which may * vary according to the version of the client.
*
* In order to help retrieving interesting data such as product name (Firefox, * IE, etc), version, operating system, Restlet users has the ability to define * their own way to extract data from the "user-agent" header. It is based on a * list of templates declared in a file called "agent.properties" and located in * the classpath in the sub directory "org/restlet/data". Each template * describes a typical user-agent string and allows to use predefined variables * that help to retrieve the content of the agent name, version, operating * system.
*
* The "user-agent" string is confronted to the each template from the beginning * of the property file to the end. The loop stops at the first matched * template.
*
* Here is a sample of such template:
* *
 * #Firefox for Windows
 *  Mozilla/{mozillaVersion} (Windows; U; {agentOs}; {osData}; rv:{releaseVersion}) Gecko/{geckoReleaseDate} {agentName}/{agentVersion}
 * 
* * This template matches the "user-agent" string of the Firefox client for * windows: * *
 *  Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20060918 Firefox/2.0
 * 
* * At this time, six predefined variables are used:
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
NameDescription
agentNameName of the user agent (i.e.: Firefox)
agentVersionVersion of the user agent
agentOsOperating system of the user agent
agentCommentComment string, that is to say a sequence of characters enclosed "(", or * ")"
commentAttributeA sequence of characters enclosed by ";", "(", or ")"
facultativeDataA sequence of characters that can be empty
*
*
* These variables are used to generate a {@link Product} instance with the main * data (name, version, comment). This instance is accessible via the * {@link ClientInfo#getMainAgentProduct()} method. All other variables used in * the template aims at catching a sequence of characters and are accessible via * the {@link ClientInfo#getAgentAttributes()} method. * * @author Jerome Louvel */ public final class ClientInfo { /** The character set preferences. */ private volatile List> acceptedCharacterSets; /** The encoding preferences. */ private volatile List> acceptedEncodings; /** The language preferences. */ private volatile List> acceptedLanguages; /** The media preferences. */ private volatile List> acceptedMediaTypes; /** The IP addresses. */ private volatile List addresses; /** The agent name. */ private volatile String agent; /** The main product data taken from the agent name. */ private volatile Product agentMainProduct; /** The attributes data taken from the agent name. */ private volatile Map agentAttributes; /** The list of product tokens taken from the agent name. */ private volatile List agentProducts; /** The port number. */ private volatile int port; /** * Constructor. */ public ClientInfo() { this.addresses = null; this.agent = null; this.port = -1; this.acceptedCharacterSets = null; this.acceptedEncodings = null; this.acceptedLanguages = null; this.acceptedMediaTypes = null; this.agentProducts = null; } /** * Returns the modifiable list of character set preferences. Creates a new * instance if no one has been set. * * @return The character set preferences. */ public List> getAcceptedCharacterSets() { // Lazy initialization with double-check. List> a = this.acceptedCharacterSets; if (a == null) { synchronized (this) { a = this.acceptedCharacterSets; if (a == null) { this.acceptedCharacterSets = a = new ArrayList>(); } } } return a; } /** * Returns the modifiable list of encoding preferences. Creates a new * instance if no one has been set. * * @return The encoding preferences. */ public List> getAcceptedEncodings() { // Lazy initialization with double-check. List> a = this.acceptedEncodings; if (a == null) { synchronized (this) { a = this.acceptedEncodings; if (a == null) { this.acceptedEncodings = a = new ArrayList>(); } } } return a; } /** * Returns the modifiable list of language preferences. Creates a new * instance if no one has been set. * * @return The language preferences. */ public List> getAcceptedLanguages() { // Lazy initialization with double-check. List> a = this.acceptedLanguages; if (a == null) { synchronized (this) { a = this.acceptedLanguages; if (a == null) { this.acceptedLanguages = a = new ArrayList>(); } } } return a; } /** * Returns the modifiable list of media type preferences. Creates a new * instance if no one has been set. * * @return The media type preferences. */ public List> getAcceptedMediaTypes() { // Lazy initialization with double-check. List> a = this.acceptedMediaTypes; if (a == null) { synchronized (this) { a = this.acceptedMediaTypes; if (a == null) { this.acceptedMediaTypes = a = new ArrayList>(); } } } return a; } /** * Returns the client's IP address which is the first address in the list of * client addresses, if this list exists and isn't empty. * * @return The client's IP address. */ public String getAddress() { return (this.addresses == null) ? null : (this.addresses.isEmpty() ? null : this.addresses.get(0)); } /** * Returns the modifiable list of client IP addresses.
*
* The first address is the one of the immediate client component as * returned by the getClientAdress() method and the last address should * correspond to the origin client (frequently a user agent).
*
* This is useful when the user agent is separated from the origin server by * a chain of intermediary components. Creates a new instance if no one has * been set. * * @return The client IP addresses. */ public List getAddresses() { // Lazy initialization with double-check. List a = this.addresses; if (a == null) { synchronized (this) { a = this.addresses; if (a == null) { this.addresses = a = new ArrayList(); } } } return a; } /** * Returns the agent name (ex: "Noelios-Restlet-Engine/1.1"). * * @return The agent name. */ public String getAgent() { return this.agent; } /** * Returns a list of attributes taken from the name of the user agent. * * @return A list of attributes taken from the name of the user agent. */ public Map getAgentAttributes() { if (this.agentAttributes == null) { this.agentAttributes = new HashMap(); final Map map = new HashMap(); // Loop on a list of user-agent templates until a template match // the current user-agent string. The list of templates is // located in a file named "agent.properties" available on // the classpath. // Some defined variables are used in order to catch the name, // version and facultative comment. Respectively, these // variables are called "agentName", "agentVersion" and // "agentComment". final URL userAgentPropertiesUrl = Engine.getClassLoader() .getResource("org/restlet/data/agent.properties"); if (userAgentPropertiesUrl != null) { BufferedReader reader; try { reader = new BufferedReader(new InputStreamReader( userAgentPropertiesUrl.openStream(), CharacterSet.UTF_8.getName())); Template template = null; // Predefined variables. final Variable agentName = new Variable(Variable.TYPE_TOKEN); final Variable agentVersion = new Variable( Variable.TYPE_TOKEN); final Variable agentComment = new Variable( Variable.TYPE_COMMENT); final Variable agentCommentAttribute = new Variable( Variable.TYPE_COMMENT_ATTRIBUTE); final Variable facultativeData = new Variable( Variable.TYPE_ALL, null, false, false); String line = reader.readLine(); for (; line != null; line = reader.readLine()) { if ((line.trim().length() > 0) && !line.trim().startsWith("#")) { template = new Template(line, Template.MODE_EQUALS); // Update the predefined variables. template.getVariables().put("agentName", agentName); template.getVariables().put("agentVersion", agentVersion); template.getVariables().put("agentComment", agentComment); template.getVariables().put("agentOs", agentCommentAttribute); template.getVariables().put("commentAttribute", agentCommentAttribute); template.getVariables().put("facultativeData", facultativeData); // Parse the template if (template.parse(getAgent(), map) > -1) { for (final String key : map.keySet()) { this.agentAttributes.put(key, (String) map .get(key)); } break; } } } reader.close(); } catch (IOException e) { return this.agentAttributes; } } } return this.agentAttributes; } /** * Returns the name of the user agent. * * @return The name of the user agent. */ public String getAgentName() { final Product product = getMainAgentProduct(); if (product != null) { return product.getName(); } return null; } /** * Returns the list of product tokens from the user agent name. * * @return The list of product tokens from the user agent name. */ public List getAgentProducts() { if (this.agentProducts == null) { this.agentProducts = Engine.getInstance() .parseUserAgent(getAgent()); } return this.agentProducts; } /** * Returns the version of the user agent. * * @return The version of the user agent. */ public String getAgentVersion() { final Product product = getMainAgentProduct(); if (product != null) { return product.getVersion(); } return null; } /** * Returns a Product object based on the name of the user agent. * * @return A Product object based on name of the user agent. */ public Product getMainAgentProduct() { if (this.agentMainProduct == null) { if (getAgentAttributes() != null) { this.agentMainProduct = new Product(getAgentAttributes().get( "agentName"), getAgentAttributes().get("agentVersion"), getAgentAttributes().get("agentComment")); } } return this.agentMainProduct; } /** * Returns the port number which sent the call. If no port is specified, -1 * is returned. * * @return The port number which sent the call. */ public int getPort() { return this.port; } /** * Returns the best variant for a given resource according the the client * preferences: accepted languages, accepted character sets, accepted media * types and accepted encodings.
* A default language is provided in case the variants don't match the * client preferences. * * @param variants * The list of variants to compare. * @param defaultLanguage * The default language. * @return The best variant. * @see Apache content negotiation algorithm */ public Variant getPreferredVariant(List variants, Language defaultLanguage) { return Engine.getInstance().getPreferredVariant(this, variants, defaultLanguage); } /** * Returns the best variant for a given resource according the the client * preferences.
* A default language is provided in case the resource's variants don't * match the client preferences. * * @param resource * The resource for which the best representation needs to be * set. * @param defaultLanguage * The default language. * @return The best variant. * @see Apache content negotiation algorithm */ public Variant getPreferredVariant(Resource resource, Language defaultLanguage) { return getPreferredVariant(resource.getVariants(), defaultLanguage); } /** * Sets the character set preferences. * * @param acceptedCharacterSets * The character set preferences. */ public void setAcceptedCharacterSets( List> acceptedCharacterSets) { this.acceptedCharacterSets = acceptedCharacterSets; } /** * Sets the encoding preferences. * * @param acceptedEncodings * The encoding preferences. */ public void setAcceptedEncodings( List> acceptedEncodings) { this.acceptedEncodings = acceptedEncodings; } /** * Sets the language preferences. * * @param acceptedLanguages * The language preferences. */ public void setAcceptedLanguages( List> acceptedLanguages) { this.acceptedLanguages = acceptedLanguages; } /** * Sets the media type preferences. * * @param acceptedMediaTypes * The media type preferences. */ public void setAcceptedMediaTypes( List> acceptedMediaTypes) { this.acceptedMediaTypes = acceptedMediaTypes; } /** * Sets the client's IP address. * * @param address * The client's IP address. */ public void setAddress(String address) { if (getAddresses().isEmpty()) { getAddresses().add(address); } else { getAddresses().set(0, address); } } /** * Sets the list of client IP addresses. * * @param addresses * The list of client IP addresses. */ public void setAddresses(List addresses) { this.addresses = addresses; } /** * Sets the agent name (ex: "Noelios Restlet Engine/1.1"). * * @param agent * The agent name. */ public void setAgent(String agent) { this.agent = agent; } /** * Sets the port number which sent the call. * * @param port * The port number which sent the call. */ public void setPort(int port) { this.port = port; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy