/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 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 Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Filter; import java.util.logging.Level; import org.restlet.engine.Engine; import org.restlet.engine.application.ApplicationHelper; import org.restlet.engine.resource.AnnotationUtils; import org.restlet.resource.ServerResource; import org.restlet.routing.Router; import org.restlet.routing.VirtualHost; import org.restlet.security.Role; import org.restlet.service.ConnectorService; import org.restlet.service.ConnegService; import org.restlet.service.ConverterService; import org.restlet.service.DecoderService; import org.restlet.service.EncoderService; import org.restlet.service.MetadataService; import org.restlet.service.RangeService; import org.restlet.service.StatusService; import org.restlet.service.TunnelService; import org.restlet.util.ServiceList; /** * Restlet managing a coherent set of resources and services. Applications are * guaranteed to receive calls with their base reference set relatively to the * {@link VirtualHost} that served them. This class is both a descriptor able to * create the root Restlet and the actual Restlet that can be attached to one or * more VirtualHost instances.<br> * <br> * Applications also have many useful services associated. Most are enabled by * default and are available as properties that can be eventually overridden: * <ul> * <li>"connectorService" to declare necessary client and server connectors.</li> * <li>"converterService" to convert between regular objects and * representations.</li> * <li>"decoderService" to automatically decode or uncompress received entities. * </li> * <li>"encoderService" to automatically encode or compress sent entities * (disabled by default).</li> * <li>"metadataService" to provide access to metadata and their associated * extension names.</li> * <li>"rangeService" to automatically exposes ranges of response entities.</li> * <li>"statusService" to provide common representations for exception status.</li> * <li>"taskService" to run tasks asynchronously (disabled by default).</li> * <li>"tunnelService" to tunnel method names or client preferences via query * parameters.</li> * </ul> * * Concurrency note: instances of this class or its subclasses can be invoked by * several threads at the same time and therefore must be thread-safe. You * should be especially careful when storing state in member variables. * * @author Jerome Louvel */ public class Application extends Restlet { private static final ThreadLocal<Application> CURRENT = new ThreadLocal<Application>(); /** * This variable is stored internally as a thread local variable and updated * each time a call enters an application. * * Warning: this method should only be used under duress. You should by * default prefer obtaining the current application using methods such as * {@link org.restlet.resource.Resource#getApplication()} * * @return The current context. */ public static Application getCurrent() { return CURRENT.get(); } /** * Sets the context to associated with the current thread. * * @param application * The thread's context. */ public static void setCurrent(Application application) { CURRENT.set(application); } /** Indicates if the debugging mode is enabled. */ private volatile boolean debugging; /** The helper provided by the implementation. */ private volatile ApplicationHelper helper; /** The inbound root Restlet. */ private volatile Restlet inboundRoot; /** The outbound root Restlet. */ private volatile Restlet outboundRoot; /** The modifiable list of roles. */ private final List<Role> roles; /** The list of services. */ private final ServiceList services; /** * Constructor. Note this constructor is convenient because you don't have * to provide a context like for {@link #Application(Context)}. Therefore * the context will initially be null. It's only when you attach the * application to a virtual host via one of its attach*() methods that a * proper context will be set. */ public Application() { this(null); } /** * Constructor. * * @param context * The context to use based on parent component context. This * context should be created using the * {@link Context#createChildContext()} method to ensure a proper * isolation with the other applications. */ public Application(Context context) { super(context); if (Engine.getInstance() != null) { this.helper = new ApplicationHelper(this); this.helper.setContext(context); } ConnegService connegService = new ConnegService(); ConverterService converterService = new ConverterService(); MetadataService metadataService = new MetadataService(); this.debugging = false; this.outboundRoot = null; this.inboundRoot = null; this.roles = new CopyOnWriteArrayList<Role>(); this.services = new ServiceList(context); this.services.add(new TunnelService(true, true)); this.services.add(new StatusService(true, converterService, metadataService, connegService)); this.services.add(new DecoderService()); this.services.add(new EncoderService(false)); this.services.add(new RangeService()); this.services.add(new ConnectorService()); this.services.add(connegService); this.services.add(converterService); this.services.add(metadataService); // [ifndef gae] this.services.add(new org.restlet.service.TaskService(false)); // [enddef] } /** * Creates a inbound root Restlet that will receive all incoming calls. In * general, instances of Router, Filter or Finder classes will be used as * initial application Restlet. The default implementation returns null by * default. This method is intended to be overridden by subclasses. * * @return The inbound root Restlet. */ public Restlet createInboundRoot() { return null; } /** * Creates a outbound root Restlet that will receive all outgoing calls from * ClientResource. In general, instances of {@link Router} and * {@link Filter} classes will be used. The default implementation returns a * Restlet giving access to the the outbound service layer and finally to * the {@link Context#getClientDispatcher()}. * <p> * This method is intended to be overridden by subclasses but in order to * benefit from the outbound service filtering layer, the original outbound * root must be careful attached again at the end of the user filtering * layer. * * @return The outbound root Restlet. */ public Restlet createOutboundRoot() { return getHelper().getFirstOutboundFilter(); } /** * Returns the connector service. The service is enabled by default. * * @return The connector service. */ public ConnectorService getConnectorService() { return getServices().get(ConnectorService.class); } /** * Returns the content negotiation service. The service is enabled by * default. * * @return The content negotiation service. */ public ConnegService getConnegService() { return getServices().get(ConnegService.class); } /** * Returns the converter service. The service is enabled by default. * * @return The converter service. */ public ConverterService getConverterService() { return getServices().get(ConverterService.class); } /** * Returns the decoder service. The service is enabled by default. * * @return The decoder service. */ public DecoderService getDecoderService() { return getServices().get(DecoderService.class); } /** * Returns the encoder service. The service is disabled by default. * * @return The encoder service. */ public EncoderService getEncoderService() { return getServices().get(EncoderService.class); } /** * Returns the helper provided by the implementation. * * @return The helper provided by the implementation. */ private ApplicationHelper getHelper() { return this.helper; } /** * Returns the inbound root Restlet. * * @return The inbound root Restlet. */ public Restlet getInboundRoot() { if (this.inboundRoot == null) { synchronized (this) { if (this.inboundRoot == null) { this.inboundRoot = createInboundRoot(); } } } return this.inboundRoot; } /** * Returns the metadata service. The service is enabled by default. * * @return The metadata service. */ public MetadataService getMetadataService() { return getServices().get(MetadataService.class); } /** * Returns the outbound root Restlet. * * @return The outbound root Restlet. */ public Restlet getOutboundRoot() { if (this.outboundRoot == null) { synchronized (this) { if (this.outboundRoot == null) { this.outboundRoot = createOutboundRoot(); } } } return this.outboundRoot; } /** * Returns the range service. * * @return The range service. */ public RangeService getRangeService() { return getServices().get(RangeService.class); } /** * Returns the role associated to the given name. * * @param name * The name of the role to find. * @return The role matched or null. */ public Role getRole(String name) { for (Role role : getRoles()) { if (role.getName().equals(name)) { return role; } } return null; } /** * Returns the modifiable list of roles. * * @return The modifiable list of roles. */ public List<Role> getRoles() { return roles; } /** * Returns the modifiable list of services. * * @return The modifiable list of services. */ public ServiceList getServices() { return services; } /** * Returns the status service. The service is enabled by default. * * @return The status service. */ public StatusService getStatusService() { return getServices().get(StatusService.class); } /** * Returns a task service to run concurrent tasks. The service is enabled by * default. * * @return A task service. * @deprecated */ // [ifndef gae] method @Deprecated public org.restlet.service.TaskService getTaskService() { return getServices().get(org.restlet.service.TaskService.class); } /** * Returns the tunnel service. The service is enabled by default. * * @return The tunnel service. */ public TunnelService getTunnelService() { return getServices().get(TunnelService.class); } @Override public void handle(Request request, Response response) { super.handle(request, response); if (getHelper() != null) { getHelper().handle(request, response); } } /** * Indicates if the debugging mode is enabled. True by default. * * @return True if the debugging mode is enabled. */ public boolean isDebugging() { return debugging; } /** * Sets the connector service. * * @param connectorService * The connector service. */ public void setConnectorService(ConnectorService connectorService) { getServices().set(connectorService); } /** * Sets the content negotiation service. * * @param connegService * The content negotiation service. */ public void setConnegService(ConnegService connegService) { getServices().set(connegService); } @Override public void setContext(Context context) { super.setContext(context); getHelper().setContext(context); getServices().setContext(context); } /** * Sets the converter service. * * @param converterService * The converter service. */ public void setConverterService(ConverterService converterService) { getServices().set(converterService); } /** * Indicates if the debugging mode is enabled. * * @param debugging * True if the debugging mode is enabled. */ public void setDebugging(boolean debugging) { this.debugging = debugging; } /** * Sets the decoder service. * * @param decoderService * The decoder service. */ public void setDecoderService(DecoderService decoderService) { getServices().set(decoderService); } /** * Sets the encoder service. * * @param encoderService * The encoder service. */ public void setEncoderService(EncoderService encoderService) { getServices().set(encoderService); } /** * Sets the inbound root Resource class. * * @param inboundRootClass * The inbound root Resource class. */ public synchronized void setInboundRoot( Class<? extends ServerResource> inboundRootClass) { setInboundRoot(createFinder(inboundRootClass)); } /** * Sets the inbound root Restlet. * * @param inboundRoot * The inbound root Restlet. */ public synchronized void setInboundRoot(Restlet inboundRoot) { this.inboundRoot = inboundRoot; if ((inboundRoot != null) && (inboundRoot.getContext() == null)) { inboundRoot.setContext(getContext()); } } /** * Sets the metadata service. * * @param metadataService * The metadata service. */ public void setMetadataService(MetadataService metadataService) { getServices().set(metadataService); } /** * Sets the outbound root Resource class. * * @param outboundRootClass * The client root {@link ServerResource} subclass. */ public synchronized void setOutboundRoot( Class<? extends ServerResource> outboundRootClass) { setOutboundRoot(createFinder(outboundRootClass)); } /** * Sets the outbound root Restlet. * * @param outboundRoot * The outbound root Restlet. */ public synchronized void setOutboundRoot(Restlet outboundRoot) { this.outboundRoot = outboundRoot; if ((outboundRoot != null) && (outboundRoot.getContext() == null)) { outboundRoot.setContext(getContext()); } } /** * Sets the range service. * * @param rangeService * The range service. */ public void setRangeService(RangeService rangeService) { getServices().set(rangeService); } /** * Sets the modifiable list of roles. This method clears the current list * and adds all entries in the parameter list. * * @param roles * A list of roles. */ public void setRoles(List<Role> roles) { synchronized (getRoles()) { if (roles != getRoles()) { getRoles().clear(); if (roles != null) { getRoles().addAll(roles); } } } } /** * Sets the status service. * * @param statusService * The status service. */ public void setStatusService(StatusService statusService) { getServices().set(statusService); } /** * Sets the task service. * * @param taskService * The task service. */ // [ifndef gae] method public void setTaskService(org.restlet.service.TaskService taskService) { getServices().set(taskService); } /** * Sets the tunnel service. * * @param tunnelService * The tunnel service. */ public void setTunnelService(TunnelService tunnelService) { getServices().set(tunnelService); } /** * Starts the application, all the enabled associated services then the * inbound and outbound roots. */ @Override public synchronized void start() throws Exception { if (isStopped()) { if (isDebugging()) { getLogger().log( Level.INFO, "Starting " + getClass().getName() + " application in debug mode"); } else { getLogger().log(Level.INFO, "Starting " + getClass().getName() + " application"); } if (getHelper() != null) { getHelper().start(); } getServices().start(); if (getInboundRoot() != null) { getInboundRoot().start(); } if (getOutboundRoot() != null) { getOutboundRoot().start(); } // Must be invoked as a last step super.start(); } } /** * Stops the application, the inbound and outbound roots then all the * enabled associated services. Finally, it clears the internal cache of * annotations. */ @Override public synchronized void stop() throws Exception { if (isStarted()) { // Must be invoked as a first step super.stop(); if (getOutboundRoot() != null) { getOutboundRoot().stop(); } if (getInboundRoot() != null) { getInboundRoot().stop(); } getServices().stop(); if (getHelper() != null) { getHelper().stop(); } // Clear the annotations cache AnnotationUtils.getInstance().clearCache(); } } }