1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320 |
- /*
- * Copyright 2000-2021 Vaadin Ltd.
- *
- * 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 com.vaadin.server;
-
- import static java.nio.charset.StandardCharsets.UTF_8;
-
- import java.io.BufferedWriter;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.io.Serializable;
- import java.lang.reflect.Constructor;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.nio.charset.StandardCharsets;
- import java.security.MessageDigest;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Locale;
- import java.util.Map;
- import java.util.ServiceLoader;
- import java.util.Set;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.CopyOnWriteArrayList;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.Future;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- import java.util.logging.Level;
- import java.util.logging.Logger;
-
- import javax.portlet.Portlet;
- import javax.portlet.PortletContext;
- import javax.servlet.Servlet;
- import javax.servlet.ServletContext;
- import javax.servlet.http.HttpServletResponse;
-
- import com.vaadin.annotations.PreserveOnRefresh;
- import com.vaadin.server.VaadinSession.FutureAccess;
- import com.vaadin.server.VaadinSession.State;
- import com.vaadin.server.communication.AtmospherePushConnection;
- import com.vaadin.server.communication.FileUploadHandler;
- import com.vaadin.server.communication.HeartbeatHandler;
- import com.vaadin.server.communication.PublishedFileHandler;
- import com.vaadin.server.communication.SessionRequestHandler;
- import com.vaadin.server.communication.UidlRequestHandler;
- import com.vaadin.shared.ApplicationConstants;
- import com.vaadin.shared.JsonConstants;
- import com.vaadin.shared.Registration;
- import com.vaadin.shared.ui.ui.UIConstants;
- import com.vaadin.ui.UI;
- import com.vaadin.util.CurrentInstance;
-
- import elemental.json.Json;
- import elemental.json.JsonException;
- import elemental.json.JsonObject;
- import elemental.json.impl.JsonUtil;
-
- /**
- * Provide deployment specific settings that are required outside terminal
- * specific code.
- *
- * @author Vaadin Ltd.
- *
- * @since 7.0
- */
- public abstract class VaadinService implements Serializable {
- /**
- * Attribute name for telling
- * {@link VaadinSession#valueUnbound(javax.servlet.http.HttpSessionBindingEvent)}
- * that it should not close a {@link VaadinSession} even though it gets
- * unbound. If a {@code VaadinSession} has an attribute with this name and
- * the attribute value is {@link Boolean#TRUE}, that session will not be
- * closed when it is unbound from the underlying session.
- */
- // Use the old name.reinitializing value for backwards compatibility
- static final String PRESERVE_UNBOUND_SESSION_ATTRIBUTE = VaadinService.class
- .getName() + ".reinitializing";
-
- /**
- * @deprecated As of 7.1.1, use {@link #PRESERVE_UNBOUND_SESSION_ATTRIBUTE}
- * instead
- */
- @Deprecated
- static final String REINITIALIZING_SESSION_MARKER = PRESERVE_UNBOUND_SESSION_ATTRIBUTE;
-
- /**
- * @deprecated As of 7.0. Only supported for {@link LegacyApplication}.
- */
- @Deprecated
- public static final String URL_PARAMETER_RESTART_APPLICATION = "restartApplication";
-
- /**
- * @deprecated As of 7.0. Only supported for {@link LegacyApplication}.
- */
- @Deprecated
- public static final String URL_PARAMETER_CLOSE_APPLICATION = "closeApplication";
-
- private static final String REQUEST_START_TIME_ATTRIBUTE = "requestStartTime";
-
- /**
- * Should never be used directly, always use
- * {@link #getDeploymentConfiguration()}
- */
- private final DeploymentConfiguration deploymentConfiguration;
-
- /*
- * Can't use EventRouter for these listeners since it's not thread safe. One
- * option would be to use an EventRouter instance guarded with a lock, but
- * then we would needlessly hold a "global" lock while invoking potentially
- * slow listener implementations.
- */
- private final Set<ServiceDestroyListener> serviceDestroyListeners = Collections
- .newSetFromMap(new ConcurrentHashMap<>());
-
- private final List<SessionInitListener> sessionInitListeners = new CopyOnWriteArrayList<>();
-
- private final List<SessionDestroyListener> sessionDestroyListeners = new CopyOnWriteArrayList<>();
-
- private SystemMessagesProvider systemMessagesProvider = DefaultSystemMessagesProvider
- .get();
-
- private ClassLoader classLoader;
-
- private Iterable<RequestHandler> requestHandlers;
- private Iterable<DependencyFilter> dependencyFilters;
- private ConnectorIdGenerator connectorIdGenerator;
-
- private Boolean atmosphereAvailable = null;
-
- /**
- * Keeps track of whether a warning about missing push support has already
- * been logged. This is used to avoid spamming the log with the same message
- * every time a new UI is bootstrapped.
- */
- private boolean pushWarningEmitted = false;
-
- /**
- * Has {@link #init()} been run?
- */
- private boolean initialized = false;
-
- /**
- * Creates a new vaadin service based on a deployment configuration.
- *
- * @param deploymentConfiguration
- * the deployment configuration for the service
- */
- public VaadinService(DeploymentConfiguration deploymentConfiguration) {
- this.deploymentConfiguration = deploymentConfiguration;
-
- final String classLoaderName = getDeploymentConfiguration()
- .getClassLoaderName();
- if (classLoaderName != null) {
- try {
- final Class<?> classLoaderClass = getClass().getClassLoader()
- .loadClass(classLoaderName);
- final Constructor<?> c = classLoaderClass
- .getConstructor(ClassLoader.class);
- setClassLoader((ClassLoader) c
- .newInstance(getClass().getClassLoader()));
- } catch (final Exception e) {
- throw new RuntimeException(
- "Could not find specified class loader: "
- + classLoaderName,
- e);
- }
- }
-
- if (getClassLoader() == null) {
- setDefaultClassLoader();
- }
- }
-
- /**
- * Creates a service. This method is for use by dependency injection
- * frameworks etc. and must be followed by a call to
- * {@link #setClassLoader(ClassLoader)} or {@link #setDefaultClassLoader()}
- * before use. Furthermore {@link #getDeploymentConfiguration()} should be
- * overridden (or otherwise intercepted) so it does not return
- * <code>null</code>.
- *
- * @since 8.2
- */
- protected VaadinService() {
- deploymentConfiguration = null;
- }
-
- /**
- * Initializes this service. The service should be initialized before it is
- * used.
- *
- * @since 7.1
- * @throws ServiceException
- * if a problem occurs when creating the service
- */
- public void init() throws ServiceException {
- List<RequestHandler> handlers = createRequestHandlers();
-
- ServiceInitEvent event = new ServiceInitEvent(this);
-
- Iterator<VaadinServiceInitListener> initListeners = getServiceInitListeners();
- while (initListeners.hasNext()) {
- initListeners.next().serviceInit(event);
- }
-
- handlers.addAll(event.getAddedRequestHandlers());
-
- Collections.reverse(handlers);
-
- requestHandlers = Collections.unmodifiableCollection(handlers);
-
- dependencyFilters = Collections.unmodifiableCollection(
- initDependencyFilters(event.getAddedDependencyFilters()));
-
- connectorIdGenerator = initConnectorIdGenerator(
- event.getAddedConnectorIdGenerators());
- assert connectorIdGenerator != null;
-
- initialized = true;
- }
-
- /**
- * Gets all available service init listeners. A custom Vaadin service
- * implementation can override this method to discover init listeners in
- * some other way in addition to the default implementation that uses
- * {@link ServiceLoader}. This could for example be used to allow defining
- * an init listener as an OSGi service or as a Spring bean.
- *
- * @since 8.0
- *
- * @return an iterator of available service init listeners
- */
- protected Iterator<VaadinServiceInitListener> getServiceInitListeners() {
- ServiceLoader<VaadinServiceInitListener> loader = ServiceLoader
- .load(VaadinServiceInitListener.class, getClassLoader());
- return loader.iterator();
- }
-
- /**
- * Called during initialization to add the request handlers for the service.
- * Note that the returned list will be reversed so the last handler will be
- * called first. This enables overriding this method and using add on the
- * returned list to add a custom request handler which overrides any
- * predefined handler.
- *
- * @return The list of request handlers used by this service.
- * @throws ServiceException
- * if a problem occurs when creating the request handlers
- */
- protected List<RequestHandler> createRequestHandlers()
- throws ServiceException {
- List<RequestHandler> handlers = new ArrayList<>();
- handlers.add(new SessionRequestHandler());
- handlers.add(new PublishedFileHandler());
- handlers.add(new HeartbeatHandler());
- handlers.add(new FileUploadHandler());
- handlers.add(new UidlRequestHandler());
- handlers.add(new UnsupportedBrowserHandler());
- handlers.add(new ConnectorResourceHandler());
-
- return handlers;
- }
-
- /**
- * Return the URL from where static files, e.g. the widgetset and the theme,
- * are served. In a standard configuration the VAADIN folder inside the
- * returned folder is what is used for widgetsets and themes.
- *
- * The returned folder is usually the same as the context path and
- * independent of e.g. the servlet mapping.
- *
- * @param request
- * the request for which the location should be determined
- *
- * @return The location of static resources (should contain the VAADIN
- * directory). Never ends with a slash (/).
- */
- public abstract String getStaticFileLocation(VaadinRequest request);
-
- /**
- * Gets the widgetset that is configured for this deployment, e.g. from a
- * parameter in web.xml.
- *
- * @param request
- * the request for which a widgetset is required
- * @return the name of the widgetset
- */
- public abstract String getConfiguredWidgetset(VaadinRequest request);
-
- /**
- * Gets the theme that is configured for this deployment, e.g. from a portal
- * parameter or just some sensible default value.
- *
- * @param request
- * the request for which a theme is required
- * @return the name of the theme
- */
- public abstract String getConfiguredTheme(VaadinRequest request);
-
- /**
- * Checks whether the UI will be rendered on its own in the browser or
- * whether it will be included into some other context. A standalone UI may
- * do things that might interfere with other parts of a page, e.g. changing
- * the page title and requesting focus upon loading.
- *
- * @param request
- * the request for which the UI is loaded
- * @return a boolean indicating whether the UI should be standalone
- */
- public abstract boolean isStandalone(VaadinRequest request);
-
- /**
- * Gets the class loader to use for loading classes loaded by name, e.g.
- * custom UI classes. This is by default the class loader that was used to
- * load the Servlet or Portlet class to which this service belongs.
- *
- * @return the class loader to use, or <code>null</code>
- *
- * @see #setClassLoader(ClassLoader)
- */
- public ClassLoader getClassLoader() {
- return classLoader;
- }
-
- /**
- * Sets the class loader to use for loading classes loaded by name, e.g.
- * custom UI classes. Invokers of this method should be careful to not break
- * any existing class loader hierarchy, e.g. by ensuring that a class loader
- * set for this service delegates to the previously set class loader if the
- * class is not found.
- *
- * @param classLoader
- * the new class loader to set, not <code>null</code>.
- *
- * @see #getClassLoader()
- */
- public void setClassLoader(ClassLoader classLoader) {
- if (classLoader == null) {
- throw new IllegalArgumentException(
- "Can not set class loader to null");
- }
- this.classLoader = classLoader;
- }
-
- /**
- * Returns the MIME type of the specified file, or null if the MIME type is
- * not known. The MIME type is determined by the configuration of the
- * container, and may be specified in a deployment descriptor. Common MIME
- * types are "text/html" and "image/gif".
- *
- * @param resourceName
- * a String specifying the name of a file
- * @return a String specifying the file's MIME type
- *
- * @see ServletContext#getMimeType(String)
- * @see PortletContext#getMimeType(String)
- */
- public abstract String getMimeType(String resourceName);
-
- /**
- * Gets the deployment configuration. Should be overridden (or otherwise
- * intercepted) if the no-arg constructor is used in order to prevent NPEs.
- *
- * @return the deployment configuration
- */
- public DeploymentConfiguration getDeploymentConfiguration() {
- return deploymentConfiguration;
- }
-
- /**
- * Sets the system messages provider to use for getting system messages to
- * display to users of this service.
- *
- * @see #getSystemMessagesProvider()
- *
- * @param systemMessagesProvider
- * the system messages provider; <code>null</code> is not
- * allowed.
- */
- public void setSystemMessagesProvider(
- SystemMessagesProvider systemMessagesProvider) {
- if (systemMessagesProvider == null) {
- throw new IllegalArgumentException(
- "SystemMessagesProvider can not be null.");
- }
- this.systemMessagesProvider = systemMessagesProvider;
- }
-
- /**
- * Gets the system messages provider currently defined for this service.
- * <p>
- * By default, the {@link DefaultSystemMessagesProvider} which always
- * provides the built-in default {@link SystemMessages} is used.
- * </p>
- *
- * @see #setSystemMessagesProvider(SystemMessagesProvider)
- * @see SystemMessagesProvider
- * @see SystemMessages
- *
- * @return the system messages provider; not <code>null</code>
- */
- public SystemMessagesProvider getSystemMessagesProvider() {
- return systemMessagesProvider;
- }
-
- /**
- * Gets the system message to use for a specific locale. This method may
- * also be implemented to use information from current instances of various
- * objects, which means that this method might return different values for
- * the same locale under different circumstances.
- *
- * @param locale
- * the desired locale for the system messages
- * @param request
- * @return the system messages to use
- */
- public SystemMessages getSystemMessages(Locale locale,
- VaadinRequest request) {
- SystemMessagesInfo systemMessagesInfo = new SystemMessagesInfo();
- systemMessagesInfo.setLocale(locale);
- systemMessagesInfo.setService(this);
- systemMessagesInfo.setRequest(request);
- return getSystemMessagesProvider()
- .getSystemMessages(systemMessagesInfo);
- }
-
- /**
- * Returns the context base directory.
- *
- * Typically an application is deployed in a such way that is has an
- * application directory. For web applications this directory is the root
- * directory of the web applications. In some cases applications might not
- * have an application directory (for example web applications running
- * inside a war).
- *
- * @return The application base directory or null if the application has no
- * base directory.
- */
- public abstract File getBaseDirectory();
-
- /**
- * Adds a listener that gets notified when a new Vaadin service session is
- * initialized for this service.
- * <p>
- * Because of the way different service instances share the same session,
- * the listener is not necessarily notified immediately when the session is
- * created but only when the first request for that session is handled by
- * this service.
- *
- * @see #removeSessionInitListener(SessionInitListener)
- * @see SessionInitListener
- *
- * @param listener
- * the Vaadin service session initialization listener
- * @return a registration object for removing the listener
- * @since 8.0
- */
- public Registration addSessionInitListener(SessionInitListener listener) {
- sessionInitListeners.add(listener);
- return () -> sessionInitListeners.remove(listener);
- }
-
- /**
- * Removes a Vaadin service session initialization listener from this
- * service.
- *
- * @see #addSessionInitListener(SessionInitListener)
- *
- * @param listener
- * the Vaadin service session initialization listener to remove.
- * @deprecated use the {@link Registration} object returned by
- * {@link #addSessionInitListener(SessionInitListener)} to
- * remove the listener
- */
- @Deprecated
- public void removeSessionInitListener(SessionInitListener listener) {
- sessionInitListeners.remove(listener);
- }
-
- /**
- * Adds a listener that gets notified when a Vaadin service session that has
- * been initialized for this service is destroyed.
- * <p>
- * The session being destroyed is locked and its UIs have been removed when
- * the listeners are called.
- *
- * @see #addSessionInitListener(SessionInitListener)
- *
- * @param listener
- * the vaadin service session destroy listener
- * @return a registration object for removing the listener
- * @since 8.0
- */
- public Registration addSessionDestroyListener(
- SessionDestroyListener listener) {
- sessionDestroyListeners.add(listener);
- return () -> sessionDestroyListeners.remove(listener);
- }
-
- /**
- * Handles destruction of the given session. Internally ensures proper
- * locking is done.
- *
- * @param vaadinSession
- * The session to destroy
- */
- public void fireSessionDestroy(VaadinSession vaadinSession) {
- final VaadinSession session = vaadinSession;
- session.access(() -> {
- if (session.getState() == State.CLOSED) {
- return;
- }
- if (session.getState() == State.OPEN) {
- closeSession(session);
- }
- List<UI> uis = new ArrayList<>(session.getUIs());
- for (final UI ui : uis) {
- ui.accessSynchronously(() -> {
- /*
- * close() called here for consistency so that it is always
- * called before a UI is removed. UI.isClosing() is thus
- * always true in UI.detach() and associated detach
- * listeners.
- */
- if (!ui.isClosing()) {
- ui.close();
- }
- session.removeUI(ui);
- });
- }
- SessionDestroyEvent event = new SessionDestroyEvent(
- VaadinService.this, session);
- for (SessionDestroyListener listener : sessionDestroyListeners) {
- try {
- listener.sessionDestroy(event);
- } catch (Exception e) {
- /*
- * for now, use the session error handler; in the future,
- * could have an API for using some other handler for
- * session init and destroy listeners
- */
- session.getErrorHandler().error(new ErrorEvent(e));
- }
- }
- session.setState(State.CLOSED);
- });
- }
-
- /**
- * Removes a Vaadin service session destroy listener from this service.
- *
- * @see #addSessionDestroyListener(SessionDestroyListener)
- *
- * @param listener
- * the vaadin service session destroy listener
- * @deprecated use the {@link Registration} object returned by
- * {@link #addSessionDestroyListener(SessionDestroyListener)} to
- * remove the listener
- */
- @Deprecated
- public void removeSessionDestroyListener(SessionDestroyListener listener) {
- sessionDestroyListeners.remove(listener);
- }
-
- /**
- * Attempts to find a Vaadin service session associated with this request.
- * <p>
- * Handles locking of the session internally to avoid creation of duplicate
- * sessions by two threads simultaneously.
- * </p>
- *
- * @param request
- * the request to get a vaadin service session for.
- *
- * @see VaadinSession
- *
- * @return the vaadin service session for the request, or <code>null</code>
- * if no session is found and this is a request for which a new
- * session shouldn't be created.
- */
- public VaadinSession findVaadinSession(VaadinRequest request)
- throws ServiceException, SessionExpiredException {
- VaadinSession vaadinSession = findOrCreateVaadinSession(request);
- if (vaadinSession == null) {
- return null;
- }
-
- VaadinSession.setCurrent(vaadinSession);
- request.setAttribute(VaadinSession.class.getName(), vaadinSession);
-
- return vaadinSession;
- }
-
- /**
- * Associates the given lock with this service and the given wrapped
- * session. This method should not be called more than once when the lock is
- * initialized for the session.
- *
- * @see #getSessionLock(WrappedSession)
- * @param wrappedSession
- * The wrapped session the lock is associated with
- * @param lock
- * The lock object
- */
- protected void setSessionLock(WrappedSession wrappedSession, Lock lock) {
- if (wrappedSession == null) {
- throw new IllegalArgumentException(
- "Can't set a lock for a null session");
- }
- Object currentSessionLock = wrappedSession
- .getAttribute(getLockAttributeName());
- assert (currentSessionLock == null
- || currentSessionLock == lock) : "Changing the lock for a session is not allowed";
-
- wrappedSession.setAttribute(getLockAttributeName(), lock);
- }
-
- /**
- * Returns the name used to store the lock in the HTTP session.
- *
- * @return The attribute name for the lock
- */
- protected String getLockAttributeName() {
- return getServiceName() + ".lock";
- }
-
- /**
- * Gets the lock instance used to lock the VaadinSession associated with the
- * given wrapped session.
- * <p>
- * This method uses the wrapped session instead of VaadinSession to be able
- * to lock even before the VaadinSession has been initialized.
- * </p>
- *
- * @param wrappedSession
- * The wrapped session
- * @return A lock instance used for locking access to the wrapped session
- */
- protected Lock getSessionLock(WrappedSession wrappedSession) {
- Object lock = wrappedSession.getAttribute(getLockAttributeName());
-
- if (lock instanceof ReentrantLock) {
- return (ReentrantLock) lock;
- }
-
- if (lock == null) {
- return null;
- }
-
- throw new RuntimeException(
- "Something else than a ReentrantLock was stored in the "
- + getLockAttributeName() + " in the session");
- }
-
- /**
- * Locks the given session for this service instance. Typically you want to
- * call {@link VaadinSession#lock()} instead of this method.
- * <p>
- * Note: The method and its signature has been changed to return lock
- * instance in Vaadin 8.14.0. If you have overriden this method, you need
- * to update your implementation.
- * <p>
- * Note: Overriding this method is not recommended, for custom lock storage
- * strategy override {@link #getSessionLock(WrappedSession)} and
- * {@link #setSessionLock(WrappedSession,Lock)} instead.
- *
- * @param wrappedSession
- * The session to lock
- * @return Lock instance
- *
- * @throws IllegalStateException
- * if the session is invalidated before it can be locked
- */
- protected Lock lockSession(WrappedSession wrappedSession) {
- Lock lock = getSessionLock(wrappedSession);
- if (lock == null) {
- /*
- * No lock found in the session attribute. Ensure only one lock is
- * created and used by everybody by doing double checked locking.
- * Assumes there is a memory barrier for the attribute (i.e. that
- * the CPU flushes its caches and reads the value directly from main
- * memory).
- */
- synchronized (VaadinService.class) {
- lock = getSessionLock(wrappedSession);
- if (lock == null) {
- lock = new ReentrantLock();
- setSessionLock(wrappedSession, lock);
- }
- }
- }
- lock.lock();
-
- try {
- // Someone might have invalidated the session between fetching the
- // lock and acquiring it. Guard for this by calling a method that's
- // specified to throw IllegalStateException if invalidated
- // (#12282)
- wrappedSession.getAttribute(getLockAttributeName());
- } catch (IllegalStateException e) {
- lock.unlock();
- throw e;
- }
- return lock;
- }
-
- /**
- * Releases the lock for the given session for this service instance.
- * Typically you want to call {@link VaadinSession#unlock()} instead of this
- * method.
- * <p>
- * Note: The method and its signature has been changed to get lock instance
- * as parameter in Vaadin 8.14.0. If you have overriden this method, you need
- * to update your implementation.
- * <p>
- * Note: Overriding this method is not recommended, for custom lock storage
- * strategy override {@link #getSessionLock(WrappedSession)} and
- * {@link #setSessionLock(WrappedSession,Lock)} instead.
- *
- * @param wrappedSession
- * The session to unlock, used only with assert
- * @param lock
- * Lock instance to unlock
- */
- protected void unlockSession(WrappedSession wrappedSession, Lock lock) {
- assert ((ReentrantLock) lock).isHeldByCurrentThread() : "Trying to unlock the session but it has not been locked by this thread";
- lock.unlock();
- }
-
- private VaadinSession findOrCreateVaadinSession(VaadinRequest request)
- throws SessionExpiredException, ServiceException {
- boolean requestCanCreateSession = requestCanCreateSession(request);
- WrappedSession wrappedSession = getWrappedSession(request,
- requestCanCreateSession);
-
- final Lock lock;
- try {
- lock = lockSession(wrappedSession);
- } catch (IllegalStateException e) {
- throw new SessionExpiredException();
- }
-
- try {
- return doFindOrCreateVaadinSession(request,
- requestCanCreateSession);
- } finally {
- unlockSession(wrappedSession, lock);
- }
-
- }
-
- /**
- * Finds or creates a Vaadin session. Assumes necessary synchronization has
- * been done by the caller to ensure this is not called simultaneously by
- * several threads.
- *
- * @param request
- * @param requestCanCreateSession
- * @return
- * @throws SessionExpiredException
- * @throws ServiceException
- */
- private VaadinSession doFindOrCreateVaadinSession(VaadinRequest request,
- boolean requestCanCreateSession)
- throws SessionExpiredException, ServiceException {
- assert ((ReentrantLock) getSessionLock(request.getWrappedSession()))
- .isHeldByCurrentThread() : "Session has not been locked by this thread";
-
- /* Find an existing session for this request. */
- VaadinSession session = getExistingSession(request,
- requestCanCreateSession);
-
- if (session != null) {
- /*
- * There is an existing session. We can use this as long as the user
- * not specifically requested to close or restart it.
- */
-
- final boolean restartApplication = hasParameter(request,
- URL_PARAMETER_RESTART_APPLICATION)
- && !hasParameter(request,
- BootstrapHandler.IGNORE_RESTART_PARAM);
- final boolean closeApplication = hasParameter(request,
- URL_PARAMETER_CLOSE_APPLICATION);
-
- if (closeApplication) {
- closeSession(session, request.getWrappedSession(false));
- return null;
- } else if (restartApplication) {
- closeSession(session, request.getWrappedSession(false));
- return createAndRegisterSession(request);
- } else {
- return session;
- }
- }
-
- // No existing session was found
-
- if (requestCanCreateSession) {
- /*
- * If the request is such that it should create a new session if one
- * as not found, we do that.
- */
- return createAndRegisterSession(request);
- } else {
- /*
- * The session was not found and a new one should not be created.
- * Assume the session has expired.
- */
- throw new SessionExpiredException();
- }
-
- }
-
- private static boolean hasParameter(VaadinRequest request,
- String parameterName) {
- return request.getParameter(parameterName) != null;
- }
-
- /**
- * Creates and registers a new VaadinSession for this service. Assumes
- * proper locking has been taken care of by the caller.
- *
- *
- * @param request
- * The request which triggered session creation.
- * @return A new VaadinSession instance
- * @throws ServiceException
- */
- private VaadinSession createAndRegisterSession(VaadinRequest request)
- throws ServiceException {
- assert ((ReentrantLock) getSessionLock(request.getWrappedSession()))
- .isHeldByCurrentThread() : "Session has not been locked by this thread";
-
- VaadinSession session = createVaadinSession(request);
-
- VaadinSession.setCurrent(session);
-
- storeSession(session, request.getWrappedSession());
-
- // Initial WebBrowser data comes from the request
- session.getBrowser().updateRequestDetails(request);
-
- // Initial locale comes from the request
- Locale locale = request.getLocale();
- session.setLocale(locale);
- session.setConfiguration(getDeploymentConfiguration());
- session.setCommunicationManager(
- new LegacyCommunicationManager(session));
-
- ServletPortletHelper.initDefaultUIProvider(session, this);
- onVaadinSessionStarted(request, session);
-
- return session;
- }
-
- /**
- * Get the base URL that should be used for sending requests back to this
- * service.
- * <p>
- * This is only used to support legacy cases.
- *
- * @param request
- * @return
- * @throws MalformedURLException
- *
- * @deprecated As of 7.0. Only used to support {@link LegacyApplication}.
- */
- @Deprecated
- protected URL getApplicationUrl(VaadinRequest request)
- throws MalformedURLException {
- return null;
- }
-
- /**
- * Creates a new Vaadin session for this service and request.
- *
- * @param request
- * The request for which to create a VaadinSession
- * @return A new VaadinSession
- * @throws ServiceException
- *
- */
- protected VaadinSession createVaadinSession(VaadinRequest request)
- throws ServiceException {
- return new VaadinSession(this);
- }
-
- private void onVaadinSessionStarted(VaadinRequest request,
- VaadinSession session) throws ServiceException {
- SessionInitEvent event = new SessionInitEvent(this, session, request);
- for (SessionInitListener listener : sessionInitListeners) {
- try {
- listener.sessionInit(event);
- } catch (Exception e) {
- /*
- * for now, use the session error handler; in the future, could
- * have an API for using some other handler for session init and
- * destroy listeners
- */
- session.getErrorHandler().error(new ErrorEvent(e));
- }
- }
-
- ServletPortletHelper.checkUiProviders(session, this);
- }
-
- private void closeSession(VaadinSession vaadinSession,
- WrappedSession session) {
- if (vaadinSession == null) {
- return;
- }
-
- if (session != null) {
- removeSession(session);
- }
- }
-
- protected VaadinSession getExistingSession(VaadinRequest request,
- boolean allowSessionCreation) throws SessionExpiredException {
-
- final WrappedSession session = getWrappedSession(request,
- allowSessionCreation);
-
- VaadinSession vaadinSession = loadSession(session);
-
- if (vaadinSession == null) {
- return null;
- }
-
- return vaadinSession;
- }
-
- /**
- * Retrieves the wrapped session for the request.
- *
- * @param request
- * The request for which to retrieve a session
- * @param requestCanCreateSession
- * true to create a new session if one currently does not exist
- * @return The retrieved (or created) wrapped session
- * @throws SessionExpiredException
- * If the request is not associated to a session and new session
- * creation is not allowed
- */
- private WrappedSession getWrappedSession(VaadinRequest request,
- boolean requestCanCreateSession) throws SessionExpiredException {
- final WrappedSession session = request
- .getWrappedSession(requestCanCreateSession);
- if (session == null) {
- throw new SessionExpiredException();
- }
- return session;
- }
-
- /**
- * Checks whether it's valid to create a new service session as a result of
- * the given request.
- *
- * @param request
- * the request
- * @return <code>true</code> if it's valid to create a new service session
- * for the request; else <code>false</code>
- */
- protected abstract boolean requestCanCreateSession(VaadinRequest request);
-
- /**
- * Gets the currently used Vaadin service. The current service is
- * automatically defined when processing requests related to the service
- * (see {@link ThreadLocal}) and in {@link VaadinSession#access(Runnable)}
- * and {@link UI#access(Runnable)}. In other cases, (e.g. from background
- * threads, the current service is not automatically defined.
- *
- * @return the current Vaadin service instance if available, otherwise
- * <code>null</code>
- *
- * @see #setCurrentInstances(VaadinRequest, VaadinResponse)
- */
- public static VaadinService getCurrent() {
- return CurrentInstance.get(VaadinService.class);
- }
-
- /**
- * Sets the this Vaadin service as the current service and also sets the
- * current Vaadin request and Vaadin response. This method is used by the
- * framework to set the current instances when a request related to the
- * service is processed and they are cleared when the request has been
- * processed.
- * <p>
- * The application developer can also use this method to define the current
- * instances outside the normal request handling, e.g. when initiating
- * custom background threads.
- * </p>
- *
- * @param request
- * the Vaadin request to set as the current request, or
- * <code>null</code> if no request should be set.
- * @param response
- * the Vaadin response to set as the current response, or
- * <code>null</code> if no response should be set.
- *
- * @see #getCurrent()
- * @see #getCurrentRequest()
- * @see #getCurrentResponse()
- */
- public void setCurrentInstances(VaadinRequest request,
- VaadinResponse response) {
- setCurrent(this);
- CurrentInstance.set(VaadinRequest.class, request);
- CurrentInstance.set(VaadinResponse.class, response);
- }
-
- /**
- * Sets the given Vaadin service as the current service.
- *
- * @param service
- */
- public static void setCurrent(VaadinService service) {
- CurrentInstance.set(VaadinService.class, service);
- }
-
- /**
- * Gets the currently processed Vaadin request. The current request is
- * automatically defined when the request is started. The current request
- * can not be used in e.g. background threads because of the way server
- * implementations reuse request instances.
- *
- * @return the current Vaadin request instance if available, otherwise
- * <code>null</code>
- *
- * @see #setCurrentInstances(VaadinRequest, VaadinResponse)
- */
- public static VaadinRequest getCurrentRequest() {
- return VaadinRequest.getCurrent();
- }
-
- /**
- * Gets the currently processed Vaadin response. The current response is
- * automatically defined when the request is started. The current response
- * can not be used in e.g. background threads because of the way server
- * implementations reuse response instances.
- *
- * @return the current Vaadin response instance if available, otherwise
- * <code>null</code>
- *
- * @see #setCurrentInstances(VaadinRequest, VaadinResponse)
- */
- public static VaadinResponse getCurrentResponse() {
- return VaadinResponse.getCurrent();
- }
-
- /**
- * Gets a unique name for this service. The name should be unique among
- * different services of the same type but the same for corresponding
- * instances running in different JVMs in a cluster. This is typically based
- * on e.g. the configured servlet's or portlet's name.
- *
- * @return the unique name of this service instance.
- */
- public abstract String getServiceName();
-
- /**
- * Finds the {@link UI} that belongs to the provided request. This is
- * generally only supported for UIDL requests as other request types are not
- * related to any particular UI or have the UI information encoded in a
- * non-standard way. The returned UI is also set as the current UI (
- * {@link UI#setCurrent(UI)}).
- *
- * @param request
- * the request for which a UI is desired
- * @return the UI belonging to the request or null if no UI is found
- *
- */
- public UI findUI(VaadinRequest request) {
- // getForSession asserts that the lock is held
- VaadinSession session = loadSession(request.getWrappedSession());
-
- // Get UI id from the request
- String uiIdString = request.getParameter(UIConstants.UI_ID_PARAMETER);
- UI ui = null;
- if (uiIdString != null && session != null) {
- int uiId = Integer.parseInt(uiIdString);
- ui = session.getUIById(uiId);
- }
-
- UI.setCurrent(ui);
- return ui;
- }
-
- /**
- * Check if the given UI should be associated with the
- * <code>window.name</code> so that it can be re-used if the browser window
- * is reloaded. This is typically determined by the UI provider which
- * typically checks the @{@link PreserveOnRefresh} annotation but UI
- * providers and ultimately VaadinService implementations may choose to
- * override the defaults.
- *
- * @param provider
- * the UI provider responsible for the UI
- * @param event
- * the UI create event with details about the UI
- *
- * @return <code>true</code> if the UI should be preserved on refresh;
- * <code>false</code> if a new UI instance should be initialized on
- * refreshed.
- */
- public boolean preserveUIOnRefresh(UIProvider provider,
- UICreateEvent event) {
- return provider.isPreservedOnRefresh(event);
- }
-
- /**
- * Discards the current session and creates a new session with the same
- * contents. The purpose of this is to introduce a new session key in order
- * to avoid session fixation attacks.
- * <p>
- * Please note that this method makes certain assumptions about how data is
- * stored in the underlying session and may thus not be compatible with some
- * environments.
- *
- * @param request
- * The Vaadin request for which the session should be
- * reinitialized
- */
- public static void reinitializeSession(VaadinRequest request) {
- WrappedSession oldSession = request.getWrappedSession();
-
- // Stores all attributes (security key, reference to this context
- // instance) so they can be added to the new session
- Set<String> attributeNames = oldSession.getAttributeNames();
- Map<String, Object> attrs = new HashMap<>(attributeNames.size() * 2);
- for (String name : attributeNames) {
- Object value = oldSession.getAttribute(name);
- if (value instanceof VaadinSession) {
- // set flag to avoid cleanup
- VaadinSession serviceSession = (VaadinSession) value;
- serviceSession.setAttribute(PRESERVE_UNBOUND_SESSION_ATTRIBUTE,
- Boolean.TRUE);
- }
- attrs.put(name, value);
- }
-
- // Invalidate the current session
- oldSession.invalidate();
-
- // Create a new session
- WrappedSession newSession = request.getWrappedSession();
-
- // Restores all attributes (security key, reference to this context
- // instance)
- for (String name : attrs.keySet()) {
- Object value = attrs.get(name);
- newSession.setAttribute(name, value);
-
- // Ensure VaadinServiceSession knows where it's stored
- if (value instanceof VaadinSession) {
- VaadinSession serviceSession = (VaadinSession) value;
- VaadinService service = serviceSession.getService();
- // Use the same lock instance in the new session
- service.setSessionLock(newSession,
- serviceSession.getLockInstance());
-
- service.storeSession(serviceSession, newSession);
- serviceSession.setAttribute(PRESERVE_UNBOUND_SESSION_ATTRIBUTE,
- null);
- }
- }
-
- }
-
- /**
- *
- * Finds the given theme resource from the web content folder or using the
- * class loader and returns a stream for it.
- *
- * @param ui
- * The ui for which to find the resource
- * @param themeName
- * The name of the theme
- * @param resource
- * The name of the resource, e.g. "layouts/mycustomlayout.html"
- * @return A stream for the resource or null if the resource was not found
- * @throws IOException
- * if a problem occurred while finding or opening the resource
- */
- public abstract InputStream getThemeResourceAsStream(UI ui,
- String themeName, String resource) throws IOException;
-
- /**
- * Creates and returns a unique ID for the DIV where the UI is to be
- * rendered.
- *
- * @param session
- * The service session to which the bootstrapped UI will belong.
- * @param request
- * The request for which a div id is needed
- * @param uiClass
- * The class of the UI that will be bootstrapped
- *
- * @return the id to use in the DOM
- */
- public abstract String getMainDivId(VaadinSession session,
- VaadinRequest request, Class<? extends UI> uiClass);
-
- /**
- * Sets the given session to be closed and all its UI state to be discarded
- * at the end of the current request, or at the end of the next request if
- * there is no ongoing one.
- * <p>
- * After the session has been discarded, any UIs that have been left open
- * will give a Session Expired error and a new session will be created for
- * serving new UIs.
- * <p>
- * To avoid causing out of sync errors, you should typically redirect to
- * some other page using {@link Page#setLocation(String)} to make the
- * browser unload the invalidated UI.
- *
- * @see SystemMessages#getSessionExpiredCaption()
- *
- * @param session
- * the session to close
- */
- public void closeSession(VaadinSession session) {
- session.close();
- }
-
- /**
- * Closes inactive UIs in the given session, removes closed UIs from the
- * session, and closes the session if it is itself inactive. This operation
- * should not be performed without first acquiring the session lock. By
- * default called at the end of each request, after sending the response.
- *
- * @param session
- * the session to clean up
- *
- * @since 8.10
- */
- public void cleanupSession(VaadinSession session) {
- if (isSessionActive(session)) {
- closeInactiveUIs(session);
- removeClosedUIs(session);
- } else {
- if (session.getState() == State.OPEN) {
- closeSession(session);
- if (session.getSession() != null) {
- getLogger().log(Level.FINE, "Closing inactive session {0}",
- session.getSession().getId());
- }
- }
- if (session.getSession() != null) {
- /*
- * If the VaadinSession has no WrappedSession then it has
- * already been removed from the HttpSession and we do not have
- * to do it again
- */
- removeSession(session.getSession());
- }
-
- /*
- * The session was destroyed during this request and therefore no
- * destroy event has yet been sent
- */
- fireSessionDestroy(session);
- }
- }
-
- /**
- * Removes those UIs from the given session for which {@link UI#isClosing()
- * isClosing} yields true.
- *
- * @param session
- */
- private void removeClosedUIs(final VaadinSession session) {
- List<UI> uis = new ArrayList<>(session.getUIs());
- for (final UI ui : uis) {
- if (ui.isClosing()) {
- ui.accessSynchronously(() -> {
- getLogger().log(Level.FINER, "Removing closed UI {0}",
- ui.getUIId());
- session.removeUI(ui);
- });
- }
- }
- }
-
- /**
- * Closes those UIs in the given session for which {@link #isUIActive}
- * yields false.
- *
- * @since 7.0.0
- */
- private void closeInactiveUIs(VaadinSession session) {
- final String sessionId = session.getSession().getId();
- for (final UI ui : session.getUIs()) {
- if (!isUIActive(ui) && !ui.isClosing()) {
- ui.accessSynchronously(() -> {
- getLogger().log(Level.FINE,
- "Closing inactive UI #{0} in session {1}",
- new Object[] { ui.getUIId(), sessionId });
- ui.close();
- });
- }
- }
- }
-
- /**
- * Returns the number of seconds that must pass without a valid heartbeat or
- * UIDL request being received from a UI before that UI is removed from its
- * session. This is a lower bound; it might take longer to close an inactive
- * UI. Returns a negative number if heartbeat is disabled and timeout never
- * occurs.
- *
- * @see DeploymentConfiguration#getHeartbeatInterval()
- *
- * @since 7.0.0
- *
- * @return The heartbeat timeout in seconds or a negative number if timeout
- * never occurs.
- */
- private int getHeartbeatTimeout() {
- // Permit three missed heartbeats before closing the UI
- return (int) (getDeploymentConfiguration().getHeartbeatInterval()
- * (3.1));
- }
-
- /**
- * Returns the number of seconds that must pass without a valid UIDL request
- * being received for the given session before the session is closed, even
- * though heartbeat requests are received. This is a lower bound; it might
- * take longer to close an inactive session.
- * <p>
- * Returns a negative number if there is no timeout. In this case heartbeat
- * requests suffice to keep the session alive, but it will still eventually
- * expire in the regular manner if there are no requests at all (see
- * {@link WrappedSession#getMaxInactiveInterval()}).
- *
- * @see DeploymentConfiguration#isCloseIdleSessions()
- * @see #getHeartbeatTimeout()
- *
- * @since 7.0.0
- *
- * @return The UIDL request timeout in seconds, or a negative number if
- * timeout never occurs.
- */
- private int getUidlRequestTimeout(VaadinSession session) {
- return getDeploymentConfiguration().isCloseIdleSessions()
- ? session.getSession().getMaxInactiveInterval()
- : -1;
- }
-
- /**
- * Returns whether the given UI is active (the client-side actively
- * communicates with the server) or whether it can be removed from the
- * session and eventually collected.
- * <p>
- * A UI is active if and only if its {@link UI#isClosing() isClosing}
- * returns false and {@link #getHeartbeatTimeout() getHeartbeatTimeout} is
- * negative or has not yet expired.
- *
- * @since 8.1
- *
- * @param ui
- * The UI whose status to check
- *
- * @return true if the UI is active, false if it could be removed.
- */
- public boolean isUIActive(UI ui) {
- if (ui.isClosing()) {
- return false;
- }
-
- // Check for long running tasks
- Lock lockInstance = ui.getSession().getLockInstance();
- if (lockInstance instanceof ReentrantLock) {
- if (((ReentrantLock) lockInstance).hasQueuedThreads()) {
- /*
- * Someone is trying to access the session. Leaving all UIs
- * alive for now. A possible kill decision will be made at a
- * later time when the session access has ended.
- */
- return true;
- }
- }
-
- // Check timeout
- long now = System.currentTimeMillis();
- int timeout = 1000 * getHeartbeatTimeout();
- return timeout < 0 || now - ui.getLastHeartbeatTimestamp() < timeout;
- }
-
- /**
- * Returns whether the given session is active or whether it can be closed.
- * <p>
- * A session is active if and only if its {@link VaadinSession#getState()}
- * returns {@link State#OPEN} and
- * {@link #getUidlRequestTimeout(VaadinSession) getUidlRequestTimeout} is
- * negative or has not yet expired.
- *
- * @param session
- * The session whose status to check
- *
- * @return true if the session is active, false if it could be closed.
- */
- private boolean isSessionActive(VaadinSession session) {
- if (session.getState() != State.OPEN || session.getSession() == null) {
- return false;
- } else {
- long now = System.currentTimeMillis();
- int timeout = 1000 * getUidlRequestTimeout(session);
- return timeout < 0
- || now - session.getLastRequestTimestamp() < timeout;
- }
- }
-
- private static final Logger getLogger() {
- return Logger.getLogger(VaadinService.class.getName());
- }
-
- /**
- * Called before the framework starts handling a request.
- *
- * @param request
- * The request
- * @param response
- * The response
- */
- public void requestStart(VaadinRequest request, VaadinResponse response) {
- if (!initialized) {
- throw new IllegalStateException(
- "Can not process requests before init() has been called");
- }
- setCurrentInstances(request, response);
- request.setAttribute(REQUEST_START_TIME_ATTRIBUTE, System.nanoTime());
- }
-
- /**
- * Called after the framework has handled a request and the response has
- * been written.
- *
- * @param request
- * The request object
- * @param response
- * The response object
- * @param session
- * The session which was used during the request or null if the
- * request did not use a session
- */
- public void requestEnd(VaadinRequest request, VaadinResponse response,
- VaadinSession session) {
- if (session != null) {
- assert VaadinSession.getCurrent() == session;
- session.lock();
- try {
- cleanupSession(session);
- final long duration = (System.nanoTime() - (Long) request
- .getAttribute(REQUEST_START_TIME_ATTRIBUTE)) / 1000000;
- session.setLastRequestDuration(duration);
- } finally {
- session.unlock();
- }
- }
- CurrentInstance.clearAll();
- }
-
- /**
- * Returns the request handlers that are registered with this service. The
- * iteration order of the returned collection is the same as the order in
- * which the request handlers will be invoked when a request is handled.
- *
- * @return a collection of request handlers in the order they are invoked
- *
- * @see #createRequestHandlers()
- *
- * @since 7.1
- */
- public Iterable<RequestHandler> getRequestHandlers() {
- return requestHandlers;
- }
-
- /**
- * Updates the list of resource dependency filters to use for the
- * application.
- * <p>
- * The filters can freely update the dependencies in any way they see fit
- * (bundle, rewrite, merge).
- * <p>
- * The framework collects filters from the {@link SessionInitEvent} where
- * session init listeners can add them. This method is called with the
- * combined list to optionally modify it, and the result is then stored by
- * the caller as the final list to use.
- * <p>
- * The filters are called in the order the session init listeners are
- * called, which is undefined. If you need a specific order, you can
- * override this method and alter the order.
- *
- * @since 8.1
- * @param sessionInitFilters
- * a list of dependency filters collected from the session init
- * event
- * @return the list of dependency filters to use for filtering resources,
- * not null
- * @throws ServiceException
- * if something went wrong while determining the filters
- *
- */
- protected List<DependencyFilter> initDependencyFilters(
- List<DependencyFilter> sessionInitFilters) throws ServiceException {
- assert sessionInitFilters != null;
-
- return sessionInitFilters;
- }
-
- /**
- * Determines the connector id generator to use for the application.
- * <p>
- * The connector id generator creates a unique id for each connector
- * attached to a UI.
- * <p>
- * The framework collects generators from the {@link SessionInitEvent} where
- * session init listeners can add them. This method is called with the
- * combined list to determine one generator to use.
- * <p>
- * If the list is empty, a default implementation based on
- * {@link VaadinSession#getNextConnectorId()} is used. If the list contains
- * one item, it is used. If there are multiple generators in the list, an
- * exception is thrown.
- *
- * @since 8.1
- * @param addedConnectorIdGenerators
- * a list of connector id generators collected from the session
- * init event, not <code>null</code>
- * @return the connector id generator to use, not <code>null</code>
- *
- * @throws ServiceException
- * if something went wrong while determining the filters, e.g.
- * if there are multiple implementations to choose from
- *
- */
- protected ConnectorIdGenerator initConnectorIdGenerator(
- List<ConnectorIdGenerator> addedConnectorIdGenerators)
- throws ServiceException {
- assert addedConnectorIdGenerators != null;
-
- switch (addedConnectorIdGenerators.size()) {
- case 0:
- return ConnectorIdGenerator::generateDefaultConnectorId;
- case 1:
- return addedConnectorIdGenerators.get(0);
- default:
- throw new ServiceException(
- "Cannot start application since there are multiple connector id generators. Remove redundant implementations from the classpath or override VaadinService.initConenctorIdGenerator to explicitly select one to use. The found generators are: "
- + addedConnectorIdGenerators);
- }
- }
-
- /**
- * Gets the filters which all resource dependencies are passed through
- * before being sent to the client for loading.
- *
- * @see #initDependencyFilters(List)
- *
- * @since 8.1
- * @return the dependency filters to pass resources dependencies through
- * before loading
- */
- public Iterable<DependencyFilter> getDependencyFilters() {
- if (dependencyFilters == null) {
- return Collections.emptyList();
- }
- return dependencyFilters;
- }
-
- /**
- * Handles the incoming request and writes the response into the response
- * object. Uses {@link #getRequestHandlers()} for handling the request.
- * <p>
- * If a session expiration is detected during request handling then each
- * {@link RequestHandler request handler} has an opportunity to handle the
- * expiration event if it implements {@link SessionExpiredHandler}. If no
- * request handler handles session expiration a default expiration message
- * will be written.
- * </p>
- *
- * @param request
- * The incoming request
- * @param response
- * The outgoing response
- * @throws ServiceException
- * Any exception that occurs during response handling will be
- * wrapped in a ServiceException
- */
- public void handleRequest(VaadinRequest request, VaadinResponse response)
- throws ServiceException {
- requestStart(request, response);
-
- VaadinSession vaadinSession = null;
- try {
- // Find out the service session this request is related to
- vaadinSession = findVaadinSession(request);
- if (vaadinSession == null) {
- return;
- }
-
- for (RequestHandler handler : getRequestHandlers()) {
- if (handler.handleRequest(vaadinSession, request, response)) {
- return;
- }
- }
-
- // Request not handled by any RequestHandler
- response.sendError(HttpServletResponse.SC_NOT_FOUND,
- "Request was not handled by any registered handler.");
-
- } catch (final SessionExpiredException e) {
- handleSessionExpired(request, response);
- } catch (final Throwable e) {
- handleExceptionDuringRequest(request, response, vaadinSession, e);
- } finally {
- requestEnd(request, response, vaadinSession);
- }
- }
-
- private void handleExceptionDuringRequest(VaadinRequest request,
- VaadinResponse response, VaadinSession vaadinSession, Throwable t)
- throws ServiceException {
- if (vaadinSession != null) {
- vaadinSession.lock();
- }
- try {
- ErrorHandler errorHandler = ErrorEvent
- .findErrorHandler(vaadinSession);
-
- if (errorHandler != null) {
- errorHandler.error(new ErrorEvent(t));
- }
-
- // if this was an UIDL request, send UIDL back to the client
- if (ServletPortletHelper.isUIDLRequest(request)) {
- SystemMessages ci = getSystemMessages(ServletPortletHelper
- .findLocale(null, vaadinSession, request), request);
- try {
- writeUncachedStringResponse(response,
- JsonConstants.JSON_CONTENT_TYPE,
- createCriticalNotificationJSON(
- ci.getInternalErrorCaption(),
- ci.getInternalErrorMessage(), null,
- ci.getInternalErrorURL()));
- } catch (IOException e) {
- // An exception occurred while writing the response. Log
- // it and continue handling only the original error.
- getLogger().log(Level.WARNING,
- "Failed to write critical notification response to the client",
- e);
- }
- } else {
- // Re-throw other exceptions
- throw new ServiceException(t);
- }
- } finally {
- if (vaadinSession != null) {
- vaadinSession.unlock();
- }
- }
-
- }
-
- /**
- * Writes the given string as a response using the given content type.
- *
- * @param response
- * The response reference
- * @param contentType
- * The content type of the response
- * @param responseString
- * The actual response
- * @throws IOException
- * If an error occurred while writing the response
- */
- public void writeStringResponse(VaadinResponse response, String contentType,
- String responseString) throws IOException {
-
- response.setContentType(contentType);
-
- final OutputStream out = response.getOutputStream();
- try (PrintWriter outWriter = new PrintWriter(
- new BufferedWriter(new OutputStreamWriter(out, UTF_8)))) {
- outWriter.print(responseString);
- }
- }
-
- /**
- * Writes the given string as a response with headers to prevent caching and
- * using the given content type.
- *
- * @param response
- * The response reference
- * @param contentType
- * The content type of the response
- * @param responseString
- * The actual response
- * @throws IOException
- * If an error occurred while writing the response
- * @since 8.3.2
- */
- public void writeUncachedStringResponse(VaadinResponse response,
- String contentType, String responseString) throws IOException {
- // Response might contain sensitive information, so prevent all forms of
- // caching
- response.setNoCacheHeaders();
-
- writeStringResponse(response, contentType, responseString);
- }
-
- /**
- * Called when the session has expired and the request handling is therefore
- * aborted.
- *
- * @param request
- * The request
- * @param response
- * The response
- * @throws ServiceException
- * Thrown if there was any problem handling the expiration of
- * the session
- */
- protected void handleSessionExpired(VaadinRequest request,
- VaadinResponse response) throws ServiceException {
- for (RequestHandler handler : getRequestHandlers()) {
- if (handler instanceof SessionExpiredHandler) {
- try {
- if (((SessionExpiredHandler) handler)
- .handleSessionExpired(request, response)) {
- return;
- }
- } catch (IOException e) {
- throw new ServiceException(
- "Handling of session expired failed", e);
- }
- }
- }
-
- // No request handlers handled the request. Write a normal HTTP response
-
- try {
- // If there is a URL, try to redirect there
- SystemMessages systemMessages = getSystemMessages(
- ServletPortletHelper.findLocale(null, null, request),
- request);
- String sessionExpiredURL = systemMessages.getSessionExpiredURL();
- if (sessionExpiredURL != null
- && (response instanceof VaadinServletResponse)) {
- ((VaadinServletResponse) response)
- .sendRedirect(sessionExpiredURL);
- } else {
- /*
- * Session expired as a result of a standard http request and we
- * have nowhere to redirect. Reloading would likely cause an
- * endless loop. This can at least happen if refreshing a
- * resource when the session has expired.
- */
-
- // Ensure that the browser does not cache expired responses.
- // iOS 6 Safari requires this (#3226)
- response.setHeader("Cache-Control", "no-cache");
- // If Content-Type is not set, browsers assume text/html and may
- // complain about the empty response body (#4167)
- response.setHeader("Content-Type", "text/plain");
-
- response.sendError(HttpServletResponse.SC_FORBIDDEN,
- "Session expired");
- }
- } catch (IOException e) {
- throw new ServiceException(e);
- }
- }
-
- /**
- * Creates a JSON message which, when sent to client as-is, will cause a
- * critical error to be shown with the given details.
- *
- * @param caption
- * The caption of the error or null to omit
- * @param message
- * The error message or null to omit
- * @param details
- * Additional error details or null to omit
- * @param url
- * A url to redirect to. If no other details are given then the
- * user will be immediately redirected to this URL. Otherwise the
- * message will be shown and the browser will redirect to the
- * given URL only after the user acknowledges the message. If
- * null then the browser will refresh the current page.
- * @return A JSON string to be sent to the client
- */
- public static String createCriticalNotificationJSON(String caption,
- String message, String details, String url) {
- String returnString = "";
- try {
- JsonObject appError = Json.createObject();
- putValueOrJsonNull(appError, "caption", caption);
- putValueOrJsonNull(appError, "url", url);
- putValueOrJsonNull(appError, "message", message);
- putValueOrJsonNull(appError, "details", details);
-
- JsonObject meta = Json.createObject();
- meta.put("appError", appError);
-
- JsonObject json = Json.createObject();
- json.put("changes", Json.createObject());
- json.put("resources", Json.createObject());
- json.put("locales", Json.createObject());
- json.put("meta", meta);
- json.put(ApplicationConstants.SERVER_SYNC_ID, -1);
- returnString = JsonUtil.stringify(json);
- } catch (JsonException e) {
- getLogger().log(Level.WARNING,
- "Error creating critical notification JSON message", e);
- }
-
- return "for(;;);[" + returnString + "]";
- }
-
- private static void putValueOrJsonNull(JsonObject json, String key,
- String value) {
- if (value == null) {
- json.put(key, Json.createNull());
- } else {
- json.put(key, value);
- }
- }
-
- /**
- * @deprecated As of 7.0. Will likely change or be removed in a future
- * version
- */
- @Deprecated
- public void criticalNotification(VaadinRequest request,
- VaadinResponse response, String caption, String message,
- String details, String url) throws IOException {
- writeUncachedStringResponse(response, JsonConstants.JSON_CONTENT_TYPE,
- createCriticalNotificationJSON(caption, message, details, url));
- }
-
- /**
- * Enables push if push support is available and push has not yet been
- * enabled.
- *
- * If push support is not available, a warning explaining the situation will
- * be logged at least the first time this method is invoked.
- *
- * @return <code>true</code> if push can be used; <code>false</code> if push
- * is not available.
- */
- public boolean ensurePushAvailable() {
- if (isAtmosphereAvailable()) {
- return true;
- } else {
- if (!pushWarningEmitted) {
- pushWarningEmitted = true;
- getLogger().log(Level.WARNING,
- Constants.ATMOSPHERE_MISSING_ERROR);
- }
- return false;
- }
- }
-
- private boolean checkAtmosphereSupport() {
- String rawVersion = AtmospherePushConnection.getAtmosphereVersion();
- if (rawVersion == null) {
- return false;
- }
-
- if (!Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION.equals(rawVersion)) {
- getLogger().log(Level.WARNING,
- Constants.INVALID_ATMOSPHERE_VERSION_WARNING,
- new Object[] {
- Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION,
- rawVersion });
- }
- return true;
- }
-
- /**
- * Checks whether Atmosphere is available for use.
- *
- * @since 7.6
- * @return true if Atmosphere is available, false otherwise
- */
- protected boolean isAtmosphereAvailable() {
- if (atmosphereAvailable == null) {
- atmosphereAvailable = checkAtmosphereSupport();
- }
- return atmosphereAvailable;
- }
-
- /**
- * Checks that another {@link VaadinSession} instance is not locked. This is
- * internally used by {@link VaadinSession#accessSynchronously(Runnable)}
- * and {@link UI#accessSynchronously(Runnable)} to help avoid causing
- * deadlocks.
- *
- * @since 7.1
- * @param session
- * the session that is being locked
- * @throws IllegalStateException
- * if the current thread holds the lock for another session
- */
- public static void verifyNoOtherSessionLocked(VaadinSession session) {
- if (isOtherSessionLocked(session)) {
- throw new IllegalStateException(
- "Can't access session while another session is locked by the same thread. This restriction is intended to help avoid deadlocks.");
- }
- }
-
- /**
- * Checks whether there might be some {@link VaadinSession} other than the
- * provided one for which the current thread holds a lock. This method might
- * not detect all cases where some other session is locked, but it should
- * cover the most typical situations.
- *
- * @since 7.2
- * @param session
- * the session that is expected to be locked
- * @return <code>true</code> if another session is also locked by the
- * current thread; <code>false</code> if no such session was found
- */
- public static boolean isOtherSessionLocked(VaadinSession session) {
- VaadinSession otherSession = VaadinSession.getCurrent();
- if (otherSession == null || otherSession == session) {
- return false;
- }
- return otherSession.hasLock();
- }
-
- /**
- * Verifies that the given CSRF token (aka double submit cookie) is valid
- * for the given session. This is used to protect against Cross Site Request
- * Forgery attacks.
- * <p>
- * This protection is enabled by default, but it might need to be disabled
- * to allow a certain type of testing. For these cases, the check can be
- * disabled by setting the init parameter
- * <code>disable-xsrf-protection</code> to <code>true</code>.
- *
- * @see DeploymentConfiguration#isXsrfProtectionEnabled()
- *
- * @since 7.1
- *
- * @param session
- * the vaadin session for which the check should be done
- * @param requestToken
- * the CSRF token provided in the request
- * @return <code>true</code> if the token is valid or if the protection is
- * disabled; <code>false</code> if protection is enabled and the
- * token is invalid
- */
- public static boolean isCsrfTokenValid(VaadinSession session,
- String requestToken) {
-
- if (session.getService().getDeploymentConfiguration()
- .isXsrfProtectionEnabled()) {
- String sessionToken = session.getCsrfToken();
-
- if (sessionToken == null || !MessageDigest.isEqual(
- sessionToken.getBytes(StandardCharsets.UTF_8),
- requestToken.getBytes(StandardCharsets.UTF_8))) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Implementation for {@link VaadinSession#access(Runnable)}. This method is
- * implemented here instead of in {@link VaadinSession} to enable overriding
- * the implementation without using a custom subclass of VaadinSession.
- *
- * @since 7.1
- * @see VaadinSession#access(Runnable)
- *
- * @param session
- * the vaadin session to access
- * @param runnable
- * the runnable to run with the session locked
- *
- * @return a future that can be used to check for task completion and to
- * cancel the task
- */
- public Future<Void> accessSession(VaadinSession session,
- Runnable runnable) {
- FutureAccess future = new FutureAccess(session, runnable);
- session.getPendingAccessQueue().add(future);
-
- ensureAccessQueuePurged(session);
-
- return future;
- }
-
- /**
- * Makes sure the pending access queue is purged for the provided session.
- * If the session is currently locked by the current thread or some other
- * thread, the queue will be purged when the session is unlocked. If the
- * lock is not held by any thread, it is acquired and the queue is purged
- * right away.
- *
- * @since 7.1.2
- * @param session
- * the session for which the access queue should be purged
- */
- public void ensureAccessQueuePurged(VaadinSession session) {
- /*
- * If no thread is currently holding the lock, pending changes for UIs
- * with automatic push would not be processed and pushed until the next
- * time there is a request or someone does an explicit push call.
- *
- * To remedy this, we try to get the lock at this point. If the lock is
- * currently held by another thread, we just back out as the queue will
- * get purged once it is released. If the lock is held by the current
- * thread, we just release it knowing that the queue gets purged once
- * the lock is ultimately released. If the lock is not held by any
- * thread and we acquire it, we just release it again to purge the queue
- * right away.
- */
- try {
- // tryLock() would be shorter, but it does not guarantee fairness
- if (session.getLockInstance().tryLock(0, TimeUnit.SECONDS)) {
- // unlock triggers runPendingAccessTasks
- session.unlock();
- }
- } catch (InterruptedException e) {
- // Just ignore
- }
- }
-
- /**
- * Purges the queue of pending access invocations enqueued with
- * {@link VaadinSession#access(Runnable)}.
- * <p>
- * This method is automatically run by the framework at appropriate
- * situations and is not intended to be used by application developers.
- *
- * @param session
- * the vaadin session to purge the queue for
- * @since 7.1
- */
- public void runPendingAccessTasks(VaadinSession session) {
- assert session.hasLock();
-
- if (session.getPendingAccessQueue().isEmpty()) {
- return;
- }
-
- FutureAccess pendingAccess;
-
- // Dump all current instances, not only the ones dumped by setCurrent
- Map<Class<?>, CurrentInstance> oldInstances = CurrentInstance
- .getInstances();
- CurrentInstance.setCurrent(session);
- try {
- while ((pendingAccess = session.getPendingAccessQueue()
- .poll()) != null) {
- if (!pendingAccess.isCancelled()) {
- pendingAccess.run();
-
- try {
- pendingAccess.get();
- } catch (Exception exception) {
- if (exception instanceof ExecutionException) {
- Throwable cause = exception.getCause();
- if (cause instanceof Exception) {
- exception = (Exception) cause;
- }
- }
- pendingAccess.handleError(exception);
- }
- }
- }
- } finally {
- CurrentInstance.clearAll();
- CurrentInstance.restoreInstances(oldInstances);
- }
- }
-
- /**
- * Adds a service destroy listener that gets notified when this service is
- * destroyed.
- * <p>
- * The listeners may be invoked in a non-deterministic order. In particular,
- * it is not guaranteed that listeners will be invoked in the order they
- * were added.
- *
- * @since 8.0
- * @param listener
- * the service destroy listener to add
- *
- * @see #destroy()
- * @see #removeServiceDestroyListener(ServiceDestroyListener)
- * @see ServiceDestroyListener
- * @return a registration object for removing the listener
- */
- public Registration addServiceDestroyListener(
- ServiceDestroyListener listener) {
- serviceDestroyListeners.add(listener);
- return () -> serviceDestroyListeners.remove(listener);
- }
-
- /**
- * Removes a service destroy listener that was previously added with
- * {@link #addServiceDestroyListener(ServiceDestroyListener)}.
- *
- * @since 7.2
- * @param listener
- * the service destroy listener to remove
- * @deprecated use the {@link Registration} object returned by
- * {@link #addServiceDestroyListener(ServiceDestroyListener)} to
- * remove the listener
- */
- @Deprecated
- public void removeServiceDestroyListener(ServiceDestroyListener listener) {
- serviceDestroyListeners.remove(listener);
- }
-
- /**
- * Called when the servlet, portlet or similar for this service is being
- * destroyed. After this method has been called, no more requests will be
- * handled by this service.
- *
- * @see #addServiceDestroyListener(ServiceDestroyListener)
- * @see Servlet#destroy()
- * @see Portlet#destroy()
- *
- * @since 7.2
- */
- public void destroy() {
- ServiceDestroyEvent event = new ServiceDestroyEvent(this);
- serviceDestroyListeners
- .forEach(listener -> listener.serviceDestroy(event));
- }
-
- /**
- * Tries to acquire default class loader and sets it as a class loader for
- * this {@link VaadinService} if found. If current security policy disallows
- * acquiring class loader instance it will log a message and re-throw
- * {@link SecurityException}
- *
- * @throws SecurityException
- * If current security policy forbids acquiring class loader
- *
- * @since 7.3.5
- */
- protected void setDefaultClassLoader() {
- try {
- setClassLoader(
- VaadinServiceClassLoaderUtil.findDefaultClassLoader());
- } catch (SecurityException e) {
- getLogger().log(Level.SEVERE,
- Constants.CANNOT_ACQUIRE_CLASSLOADER_SEVERE, e);
- throw e;
- }
- }
-
- /**
- * Called when the VaadinSession should be stored.
- * <p>
- * By default stores the VaadinSession in the underlying HTTP session.
- *
- * @since 7.6
- * @param session
- * the VaadinSession to store
- * @param wrappedSession
- * the underlying HTTP session
- */
- protected void storeSession(VaadinSession session,
- WrappedSession wrappedSession) {
- assert VaadinSession.hasLock(this, wrappedSession);
- writeToHttpSession(wrappedSession, session);
- session.refreshTransients(wrappedSession, this);
- }
-
- /**
- * Performs the actual write of the VaadinSession to the underlying HTTP
- * session after sanity checks have been performed.
- * <p>
- * Called by {@link #storeSession(VaadinSession, WrappedSession)}
- *
- * @since 7.6
- * @param wrappedSession
- * the underlying HTTP session
- * @param session
- * the VaadinSession to store
- */
- protected void writeToHttpSession(WrappedSession wrappedSession,
- VaadinSession session) {
- wrappedSession.setAttribute(getSessionAttributeName(), session);
- }
-
- /**
- * Called when the VaadinSession should be loaded from the underlying HTTP
- * session.
- *
- * @since 7.6
- * @param wrappedSession
- * the underlying HTTP session
- * @return the VaadinSession in the HTTP session or null if not found
- */
- protected VaadinSession loadSession(WrappedSession wrappedSession) {
- assert VaadinSession.hasLock(this, wrappedSession);
-
- VaadinSession vaadinSession = readFromHttpSession(wrappedSession);
- if (vaadinSession == null) {
- return null;
- }
- vaadinSession.refreshTransients(wrappedSession, this);
- return vaadinSession;
- }
-
- /**
- * Performs the actual read of the VaadinSession from the underlying HTTP
- * session after sanity checks have been performed.
- * <p>
- * Called by {@link #loadSession(WrappedSession)}.
- *
- * @param wrappedSession
- * the underlying HTTP session
- * @since 7.6
- * @return the VaadinSession or null if no session was found
- */
- protected VaadinSession readFromHttpSession(WrappedSession wrappedSession) {
- return (VaadinSession) wrappedSession
- .getAttribute(getSessionAttributeName());
- }
-
- /**
- * Called when the VaadinSession should be removed from the underlying HTTP
- * session.
- *
- * @since 7.6
- * @param wrappedSession
- * the underlying HTTP session
- */
- public void removeSession(WrappedSession wrappedSession) {
- assert VaadinSession.hasLock(this, wrappedSession);
- removeFromHttpSession(wrappedSession);
- }
-
- /**
- * Performs the actual removal of the VaadinSession from the underlying HTTP
- * session after sanity checks have been performed.
- *
- * @since 7.6
- * @param wrappedSession
- * the underlying HTTP session
- */
- protected void removeFromHttpSession(WrappedSession wrappedSession) {
- wrappedSession.removeAttribute(getSessionAttributeName());
-
- }
-
- /**
- * Returns the name used for storing the VaadinSession in the underlying
- * HTTP session.
- *
- * @since 7.6
- * @return the attribute name used for storing the VaadinSession
- */
- protected String getSessionAttributeName() {
- return VaadinSession.class.getName() + "." + getServiceName();
- }
-
- /**
- * Generates a unique id to use for a newly attached connector.
- *
- * @see ConnectorIdGenerator
- * @see #initConnectorIdGenerator(List)
- *
- * @since 8.1
- *
- * @param session
- * the session to which the connector has been attached, not
- * <code>null</code>
- * @param connector
- * the attached connector for which to generate an id, not
- * <code>null</code>
- * @return a string id that is unique within the session, not
- * <code>null</code>
- */
- public String generateConnectorId(VaadinSession session,
- ClientConnector connector) {
- assert session.getService() == this;
- String connectorId = connectorIdGenerator.generateConnectorId(
- new ConnectorIdGenerationEvent(session, connector));
-
- assert connectorId != null;
-
- return connectorId;
- }
-
- }
|