You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractClientConnector.java 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.server;
  17. import java.io.IOException;
  18. import java.io.Serializable;
  19. import java.lang.reflect.Constructor;
  20. import java.lang.reflect.InvocationHandler;
  21. import java.lang.reflect.Method;
  22. import java.lang.reflect.Proxy;
  23. import java.util.ArrayList;
  24. import java.util.Collection;
  25. import java.util.Collections;
  26. import java.util.EventObject;
  27. import java.util.HashMap;
  28. import java.util.Iterator;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.NoSuchElementException;
  32. import java.util.logging.Logger;
  33. import com.vaadin.event.EventRouter;
  34. import com.vaadin.event.MethodEventSource;
  35. import com.vaadin.shared.communication.ClientRpc;
  36. import com.vaadin.shared.communication.ServerRpc;
  37. import com.vaadin.shared.communication.SharedState;
  38. import com.vaadin.shared.ui.ComponentStateUtil;
  39. import com.vaadin.ui.Component;
  40. import com.vaadin.ui.Component.Event;
  41. import com.vaadin.ui.HasComponents;
  42. import com.vaadin.ui.LegacyComponent;
  43. import com.vaadin.ui.UI;
  44. import elemental.json.JsonObject;
  45. /**
  46. * An abstract base class for ClientConnector implementations. This class
  47. * provides all the basic functionality required for connectors.
  48. *
  49. * @author Vaadin Ltd
  50. * @since 7.0.0
  51. */
  52. public abstract class AbstractClientConnector implements ClientConnector,
  53. MethodEventSource {
  54. /**
  55. * A map from client to server RPC interface class name to the RPC call
  56. * manager that handles incoming RPC calls for that interface.
  57. */
  58. private Map<String, ServerRpcManager<?>> rpcManagerMap = new HashMap<String, ServerRpcManager<?>>();
  59. /**
  60. * A map from server to client RPC interface class to the RPC proxy that
  61. * sends ourgoing RPC calls for that interface.
  62. */
  63. private Map<Class<?>, ClientRpc> rpcProxyMap = new HashMap<Class<?>, ClientRpc>();
  64. /**
  65. * Shared state object to be communicated from the server to the client when
  66. * modified.
  67. */
  68. private SharedState sharedState;
  69. private Class<? extends SharedState> stateType;
  70. /**
  71. * Pending RPC method invocations to be sent.
  72. */
  73. private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
  74. private String connectorId;
  75. private ArrayList<Extension> extensions = new ArrayList<Extension>();
  76. /**
  77. * The EventRouter used for the event model.
  78. */
  79. private EventRouter eventRouter = null;
  80. private ErrorHandler errorHandler = null;
  81. @Override
  82. public void addAttachListener(AttachListener listener) {
  83. addListener(AttachEvent.ATTACH_EVENT_IDENTIFIER, AttachEvent.class,
  84. listener, AttachListener.attachMethod);
  85. }
  86. @Override
  87. public void removeAttachListener(AttachListener listener) {
  88. removeListener(AttachEvent.ATTACH_EVENT_IDENTIFIER, AttachEvent.class,
  89. listener);
  90. }
  91. @Override
  92. public void addDetachListener(DetachListener listener) {
  93. addListener(DetachEvent.DETACH_EVENT_IDENTIFIER, DetachEvent.class,
  94. listener, DetachListener.detachMethod);
  95. }
  96. @Override
  97. public void removeDetachListener(DetachListener listener) {
  98. removeListener(DetachEvent.DETACH_EVENT_IDENTIFIER, DetachEvent.class,
  99. listener);
  100. }
  101. /**
  102. * @deprecated As of 7.0, use {@link #markAsDirty()} instead. Note that you
  103. * typically do not need to call {@link #markAsDirty()} as
  104. * {@link #getState()} will mark the connector dirty and the
  105. * framework will then check what, if anything, needs to be sent
  106. * to the client. {@link LegacyComponent}s which rely on paint
  107. * might still need to call this or {@link #markAsDirty()} .
  108. */
  109. @Deprecated
  110. @Override
  111. public void requestRepaint() {
  112. markAsDirty();
  113. }
  114. /* Documentation copied from interface */
  115. @Override
  116. public void markAsDirty() {
  117. assert getSession() == null || getSession().hasLock() : buildLockAssertMessage("markAsDirty()");
  118. UI uI = getUI();
  119. if (uI != null) {
  120. uI.getConnectorTracker().markDirty(this);
  121. }
  122. }
  123. private String buildLockAssertMessage(String method) {
  124. if (VaadinService.isOtherSessionLocked(getSession())) {
  125. return "The session of this connecor is not locked, but there is another session that is locked. "
  126. + "This might be caused by accidentally using a connector that belongs to another session.";
  127. } else {
  128. return "Session must be locked when " + method + " is called";
  129. }
  130. }
  131. /**
  132. * Registers an RPC interface implementation for this component.
  133. *
  134. * A component can listen to multiple RPC interfaces, and subclasses can
  135. * register additional implementations.
  136. *
  137. * @since 7.0
  138. *
  139. * @param implementation
  140. * RPC interface implementation
  141. * @param rpcInterfaceType
  142. * RPC interface class for which the implementation should be
  143. * registered
  144. */
  145. protected <T extends ServerRpc> void registerRpc(T implementation,
  146. Class<T> rpcInterfaceType) {
  147. rpcManagerMap.put(rpcInterfaceType.getName(), new ServerRpcManager<T>(
  148. implementation, rpcInterfaceType));
  149. }
  150. /**
  151. * Registers an RPC interface implementation for this component.
  152. *
  153. * A component can listen to multiple RPC interfaces, and subclasses can
  154. * register additional implementations.
  155. *
  156. * @since 7.0
  157. *
  158. * @param implementation
  159. * RPC interface implementation. Also used to deduce the type.
  160. */
  161. protected <T extends ServerRpc> void registerRpc(T implementation) {
  162. Class<?> cls = implementation.getClass();
  163. Class<?>[] interfaces = cls.getInterfaces();
  164. while (interfaces.length == 0) {
  165. // Search upwards until an interface is found. It must be found as T
  166. // extends ServerRpc
  167. cls = cls.getSuperclass();
  168. interfaces = cls.getInterfaces();
  169. }
  170. if (interfaces.length != 1
  171. || !(ServerRpc.class.isAssignableFrom(interfaces[0]))) {
  172. throw new RuntimeException(
  173. "Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface");
  174. }
  175. @SuppressWarnings("unchecked")
  176. Class<T> type = (Class<T>) interfaces[0];
  177. registerRpc(implementation, type);
  178. }
  179. /**
  180. * Returns the shared state for this connector. The shared state object is
  181. * shared between the server connector and the client connector. Changes are
  182. * only communicated from the server to the client and not in the other
  183. * direction.
  184. * <p>
  185. * As a side effect, marks the connector dirty so any changes done to the
  186. * state will be sent to the client. Use {@code getState(false)} to avoid
  187. * marking the connector as dirty.
  188. * </p>
  189. *
  190. * @return The shared state for this connector. Never null.
  191. */
  192. protected SharedState getState() {
  193. return getState(true);
  194. }
  195. /**
  196. * Returns the shared state for this connector.
  197. *
  198. * @param markAsDirty
  199. * true if the connector should automatically be marked dirty,
  200. * false otherwise
  201. *
  202. * @return The shared state for this connector. Never null.
  203. * @see #getState()
  204. */
  205. protected SharedState getState(boolean markAsDirty) {
  206. assert getSession() == null || getSession().hasLock() : buildLockAssertMessage("getState()");
  207. if (null == sharedState) {
  208. sharedState = createState();
  209. }
  210. if (markAsDirty) {
  211. UI ui = getUI();
  212. if (ui != null && !ui.getConnectorTracker().isDirty(this)
  213. && !ui.getConnectorTracker().isWritingResponse()) {
  214. ui.getConnectorTracker().markDirty(this);
  215. }
  216. }
  217. return sharedState;
  218. }
  219. @Override
  220. public JsonObject encodeState() {
  221. return LegacyCommunicationManager.encodeState(this, getState(false));
  222. }
  223. /**
  224. * Creates the shared state bean to be used in server to client
  225. * communication.
  226. * <p>
  227. * By default a state object of the defined return type of
  228. * {@link #getState()} is created. Subclasses can override this method and
  229. * return a new instance of the correct state class but this should rarely
  230. * be necessary.
  231. * </p>
  232. * <p>
  233. * No configuration of the values of the state should be performed in
  234. * {@link #createState()}.
  235. *
  236. * @since 7.0
  237. *
  238. * @return new shared state object
  239. */
  240. protected SharedState createState() {
  241. try {
  242. return getStateType().newInstance();
  243. } catch (Exception e) {
  244. throw new RuntimeException(
  245. "Error creating state of type " + getStateType().getName()
  246. + " for " + getClass().getName(), e);
  247. }
  248. }
  249. @Override
  250. public Class<? extends SharedState> getStateType() {
  251. // Lazy load because finding type can be expensive because of the
  252. // exceptions flying around
  253. if (stateType == null) {
  254. stateType = findStateType();
  255. }
  256. return stateType;
  257. }
  258. private Class<? extends SharedState> findStateType() {
  259. try {
  260. Class<?> class1 = getClass();
  261. while (class1 != null) {
  262. try {
  263. Method m = class1.getDeclaredMethod("getState",
  264. (Class[]) null);
  265. Class<?> type = m.getReturnType();
  266. if (!m.isSynthetic()) {
  267. return type.asSubclass(SharedState.class);
  268. }
  269. } catch (NoSuchMethodException nsme) {
  270. }
  271. // Try in superclass instead
  272. class1 = class1.getSuperclass();
  273. }
  274. throw new NoSuchMethodException(getClass().getCanonicalName()
  275. + ".getState()");
  276. } catch (Exception e) {
  277. throw new RuntimeException("Error finding state type for "
  278. + getClass().getName(), e);
  279. }
  280. }
  281. /**
  282. * Returns an RPC proxy for a given server to client RPC interface for this
  283. * component.
  284. *
  285. * TODO more javadoc, subclasses, ...
  286. *
  287. * @param rpcInterface
  288. * RPC interface type
  289. *
  290. * @since 7.0
  291. */
  292. protected <T extends ClientRpc> T getRpcProxy(final Class<T> rpcInterface) {
  293. // create, initialize and return a dynamic proxy for RPC
  294. try {
  295. if (!rpcProxyMap.containsKey(rpcInterface)) {
  296. Class<?> proxyClass = Proxy.getProxyClass(
  297. rpcInterface.getClassLoader(), rpcInterface);
  298. Constructor<?> constructor = proxyClass
  299. .getConstructor(InvocationHandler.class);
  300. T rpcProxy = rpcInterface.cast(constructor
  301. .newInstance(new RpcInvocationHandler(rpcInterface)));
  302. // cache the proxy
  303. rpcProxyMap.put(rpcInterface, rpcProxy);
  304. }
  305. return (T) rpcProxyMap.get(rpcInterface);
  306. } catch (Exception e) {
  307. // TODO exception handling?
  308. throw new RuntimeException(e);
  309. }
  310. }
  311. private class RpcInvocationHandler implements InvocationHandler,
  312. Serializable {
  313. private String rpcInterfaceName;
  314. public RpcInvocationHandler(Class<?> rpcInterface) {
  315. rpcInterfaceName = rpcInterface.getName().replaceAll("\\$", ".");
  316. }
  317. @Override
  318. public Object invoke(Object proxy, Method method, Object[] args)
  319. throws Throwable {
  320. if (method.getDeclaringClass() == Object.class) {
  321. // Don't add Object methods such as toString and hashCode as
  322. // invocations
  323. return method.invoke(this, args);
  324. }
  325. addMethodInvocationToQueue(rpcInterfaceName, method, args);
  326. return null;
  327. }
  328. }
  329. /**
  330. * For internal use: adds a method invocation to the pending RPC call queue.
  331. *
  332. * @param interfaceName
  333. * RPC interface name
  334. * @param method
  335. * RPC method
  336. * @param parameters
  337. * RPC all parameters
  338. *
  339. * @since 7.0
  340. */
  341. protected void addMethodInvocationToQueue(String interfaceName,
  342. Method method, Object[] parameters) {
  343. // add to queue
  344. pendingInvocations.add(new ClientMethodInvocation(this, interfaceName,
  345. method, parameters));
  346. // TODO no need to do full repaint if only RPC calls
  347. requestRepaint();
  348. }
  349. @Override
  350. public ServerRpcManager<?> getRpcManager(String rpcInterfaceName) {
  351. return rpcManagerMap.get(rpcInterfaceName);
  352. }
  353. @Override
  354. public List<ClientMethodInvocation> retrievePendingRpcCalls() {
  355. if (pendingInvocations.isEmpty()) {
  356. return Collections.emptyList();
  357. } else {
  358. List<ClientMethodInvocation> result = pendingInvocations;
  359. pendingInvocations = new ArrayList<ClientMethodInvocation>();
  360. return Collections.unmodifiableList(result);
  361. }
  362. }
  363. @Override
  364. public String getConnectorId() {
  365. if (connectorId == null) {
  366. if (getSession() == null) {
  367. throw new RuntimeException(
  368. "Component must be attached to a session when getConnectorId() is called for the first time");
  369. }
  370. connectorId = getSession().createConnectorId(this);
  371. }
  372. return connectorId;
  373. }
  374. /**
  375. * Finds the {@link VaadinSession} to which this connector belongs. If the
  376. * connector has not been attached, <code>null</code> is returned.
  377. *
  378. * @return The connector's session, or <code>null</code> if not attached
  379. */
  380. protected VaadinSession getSession() {
  381. UI uI = getUI();
  382. if (uI == null) {
  383. return null;
  384. } else {
  385. return uI.getSession();
  386. }
  387. }
  388. /**
  389. * Finds a UI ancestor of this connector. <code>null</code> is returned if
  390. * no UI ancestor is found (typically because the connector is not attached
  391. * to a proper hierarchy).
  392. *
  393. * @return the UI ancestor of this connector, or <code>null</code> if none
  394. * is found.
  395. */
  396. @Override
  397. public UI getUI() {
  398. ClientConnector connector = this;
  399. while (connector != null) {
  400. if (connector instanceof UI) {
  401. return (UI) connector;
  402. }
  403. connector = connector.getParent();
  404. }
  405. return null;
  406. }
  407. private static Logger getLogger() {
  408. return Logger.getLogger(AbstractClientConnector.class.getName());
  409. }
  410. /**
  411. * @deprecated As of 7.0, use {@link #markAsDirtyRecursive()} instead
  412. */
  413. @Override
  414. @Deprecated
  415. public void requestRepaintAll() {
  416. markAsDirtyRecursive();
  417. }
  418. @Override
  419. public void markAsDirtyRecursive() {
  420. markAsDirty();
  421. for (ClientConnector connector : getAllChildrenIterable(this)) {
  422. connector.markAsDirtyRecursive();
  423. }
  424. }
  425. /**
  426. * Get an Iterable for iterating over all child connectors, including both
  427. * extensions and child components.
  428. *
  429. * @param connector
  430. * the connector to get children for
  431. * @return an Iterable giving all child connectors.
  432. */
  433. public static Iterable<? extends ClientConnector> getAllChildrenIterable(
  434. final ClientConnector connector) {
  435. Collection<Extension> extensions = connector.getExtensions();
  436. boolean hasComponents = connector instanceof HasComponents;
  437. boolean hasExtensions = extensions.size() > 0;
  438. if (!hasComponents && !hasExtensions) {
  439. // If has neither component nor extensions, return immutable empty
  440. // list as iterable.
  441. return Collections.emptyList();
  442. }
  443. if (hasComponents && !hasExtensions) {
  444. // only components
  445. return (HasComponents) connector;
  446. }
  447. if (!hasComponents && hasExtensions) {
  448. // only extensions
  449. return extensions;
  450. }
  451. // combine the iterators of extensions and components to a new iterable.
  452. final Iterator<Component> componentsIterator = ((HasComponents) connector)
  453. .iterator();
  454. final Iterator<Extension> extensionsIterator = extensions.iterator();
  455. Iterable<? extends ClientConnector> combinedIterable = new Iterable<ClientConnector>() {
  456. @Override
  457. public Iterator<ClientConnector> iterator() {
  458. return new Iterator<ClientConnector>() {
  459. @Override
  460. public boolean hasNext() {
  461. return componentsIterator.hasNext()
  462. || extensionsIterator.hasNext();
  463. }
  464. @Override
  465. public ClientConnector next() {
  466. if (componentsIterator.hasNext()) {
  467. return componentsIterator.next();
  468. }
  469. if (extensionsIterator.hasNext()) {
  470. return extensionsIterator.next();
  471. }
  472. throw new NoSuchElementException();
  473. }
  474. @Override
  475. public void remove() {
  476. throw new UnsupportedOperationException();
  477. }
  478. };
  479. }
  480. };
  481. return combinedIterable;
  482. }
  483. @Override
  484. public Collection<Extension> getExtensions() {
  485. return Collections.unmodifiableCollection(extensions);
  486. }
  487. /**
  488. * Add an extension to this connector. This method is protected to allow
  489. * extensions to select which targets they can extend.
  490. *
  491. * @param extension
  492. * the extension to add
  493. */
  494. protected void addExtension(Extension extension) {
  495. ClientConnector previousParent = extension.getParent();
  496. if (previousParent == this) {
  497. // Nothing to do, already attached
  498. return;
  499. } else if (previousParent != null) {
  500. throw new IllegalStateException(
  501. "Moving an extension from one parent to another is not supported");
  502. }
  503. extensions.add(extension);
  504. extension.setParent(this);
  505. markAsDirty();
  506. }
  507. @Override
  508. public void removeExtension(Extension extension) {
  509. extension.setParent(null);
  510. extensions.remove(extension);
  511. markAsDirty();
  512. }
  513. /*
  514. * (non-Javadoc)
  515. *
  516. * @see com.vaadin.server.ClientConnector#isAttached()
  517. */
  518. @Override
  519. public boolean isAttached() {
  520. return getSession() != null;
  521. }
  522. @Override
  523. public void attach() {
  524. markAsDirty();
  525. getUI().getConnectorTracker().registerConnector(this);
  526. fireEvent(new AttachEvent(this));
  527. for (ClientConnector connector : getAllChildrenIterable(this)) {
  528. connector.attach();
  529. }
  530. }
  531. /**
  532. * {@inheritDoc}
  533. *
  534. * <p>
  535. * The {@link #getSession()} and {@link #getUI()} methods might return
  536. * <code>null</code> after this method is called.
  537. * </p>
  538. */
  539. @Override
  540. public void detach() {
  541. for (ClientConnector connector : getAllChildrenIterable(this)) {
  542. connector.detach();
  543. }
  544. fireEvent(new DetachEvent(this));
  545. getUI().getConnectorTracker().unregisterConnector(this);
  546. }
  547. @Override
  548. public boolean isConnectorEnabled() {
  549. if (getParent() == null) {
  550. // No parent -> the component cannot receive updates from the client
  551. return false;
  552. } else {
  553. return getParent().isConnectorEnabled();
  554. }
  555. }
  556. @Override
  557. public void beforeClientResponse(boolean initial) {
  558. // Do nothing by default
  559. }
  560. @Override
  561. public boolean handleConnectorRequest(VaadinRequest request,
  562. VaadinResponse response, String path) throws IOException {
  563. DownloadStream stream = null;
  564. String[] parts = path.split("/", 2);
  565. String key = parts[0];
  566. VaadinSession session = getSession();
  567. session.lock();
  568. try {
  569. ConnectorResource resource = (ConnectorResource) getResource(key);
  570. if (resource == null) {
  571. return false;
  572. }
  573. stream = resource.getStream();
  574. } finally {
  575. session.unlock();
  576. }
  577. stream.writeResponse(request, response);
  578. return true;
  579. }
  580. /**
  581. * Gets a resource defined using {@link #setResource(String, Resource)} with
  582. * the corresponding key.
  583. *
  584. * @param key
  585. * the string identifier of the resource
  586. * @return a resource, or <code>null</code> if there's no resource
  587. * associated with the given key
  588. *
  589. * @see #setResource(String, Resource)
  590. */
  591. protected Resource getResource(String key) {
  592. return ResourceReference
  593. .getResource(getState(false).resources.get(key));
  594. }
  595. /**
  596. * Registers a resource with this connector using the given key. This will
  597. * make the URL for retrieving the resource available to the client-side
  598. * connector using
  599. * {@link com.vaadin.terminal.gwt.client.ui.AbstractConnector#getResourceUrl(String)}
  600. * with the same key.
  601. *
  602. * @param key
  603. * the string key to associate the resource with
  604. * @param resource
  605. * the resource to set, or <code>null</code> to clear a previous
  606. * association.
  607. */
  608. protected void setResource(String key, Resource resource) {
  609. ResourceReference resourceReference = ResourceReference.create(
  610. resource, this, key);
  611. if (resourceReference == null) {
  612. getState().resources.remove(key);
  613. } else {
  614. getState().resources.put(key, resourceReference);
  615. }
  616. }
  617. /* Listener code starts. Should be refactored. */
  618. /**
  619. * <p>
  620. * Registers a new listener with the specified activation method to listen
  621. * events generated by this component. If the activation method does not
  622. * have any arguments the event object will not be passed to it when it's
  623. * called.
  624. * </p>
  625. *
  626. * <p>
  627. * This method additionally informs the event-api to route events with the
  628. * given eventIdentifier to the components handleEvent function call.
  629. * </p>
  630. *
  631. * <p>
  632. * For more information on the inheritable event mechanism see the
  633. * {@link com.vaadin.event com.vaadin.event package documentation}.
  634. * </p>
  635. *
  636. * @param eventIdentifier
  637. * the identifier of the event to listen for
  638. * @param eventType
  639. * the type of the listened event. Events of this type or its
  640. * subclasses activate the listener.
  641. * @param target
  642. * the object instance who owns the activation method.
  643. * @param method
  644. * the activation method.
  645. *
  646. * @since 6.2
  647. */
  648. protected void addListener(String eventIdentifier, Class<?> eventType,
  649. Object target, Method method) {
  650. if (eventRouter == null) {
  651. eventRouter = new EventRouter();
  652. }
  653. boolean needRepaint = !eventRouter.hasListeners(eventType);
  654. eventRouter.addListener(eventType, target, method);
  655. if (needRepaint) {
  656. ComponentStateUtil.addRegisteredEventListener(getState(),
  657. eventIdentifier);
  658. }
  659. }
  660. /**
  661. * Checks if the given {@link Event} type is listened for this component.
  662. *
  663. * @param eventType
  664. * the event type to be checked
  665. * @return true if a listener is registered for the given event type
  666. */
  667. protected boolean hasListeners(Class<?> eventType) {
  668. return eventRouter != null && eventRouter.hasListeners(eventType);
  669. }
  670. /**
  671. * Removes all registered listeners matching the given parameters. Since
  672. * this method receives the event type and the listener object as
  673. * parameters, it will unregister all <code>object</code>'s methods that are
  674. * registered to listen to events of type <code>eventType</code> generated
  675. * by this component.
  676. *
  677. * <p>
  678. * This method additionally informs the event-api to stop routing events
  679. * with the given eventIdentifier to the components handleEvent function
  680. * call.
  681. * </p>
  682. *
  683. * <p>
  684. * For more information on the inheritable event mechanism see the
  685. * {@link com.vaadin.event com.vaadin.event package documentation}.
  686. * </p>
  687. *
  688. * @param eventIdentifier
  689. * the identifier of the event to stop listening for
  690. * @param eventType
  691. * the exact event type the <code>object</code> listens to.
  692. * @param target
  693. * the target object that has registered to listen to events of
  694. * type <code>eventType</code> with one or more methods.
  695. *
  696. * @since 6.2
  697. */
  698. protected void removeListener(String eventIdentifier, Class<?> eventType,
  699. Object target) {
  700. if (eventRouter != null) {
  701. eventRouter.removeListener(eventType, target);
  702. if (!eventRouter.hasListeners(eventType)) {
  703. ComponentStateUtil.removeRegisteredEventListener(getState(),
  704. eventIdentifier);
  705. }
  706. }
  707. }
  708. /**
  709. * <p>
  710. * Registers a new listener with the specified activation method to listen
  711. * events generated by this component. If the activation method does not
  712. * have any arguments the event object will not be passed to it when it's
  713. * called.
  714. * </p>
  715. *
  716. * <p>
  717. * For more information on the inheritable event mechanism see the
  718. * {@link com.vaadin.event com.vaadin.event package documentation}.
  719. * </p>
  720. *
  721. * @param eventType
  722. * the type of the listened event. Events of this type or its
  723. * subclasses activate the listener.
  724. * @param target
  725. * the object instance who owns the activation method.
  726. * @param method
  727. * the activation method.
  728. *
  729. */
  730. @Override
  731. public void addListener(Class<?> eventType, Object target, Method method) {
  732. if (eventRouter == null) {
  733. eventRouter = new EventRouter();
  734. }
  735. eventRouter.addListener(eventType, target, method);
  736. }
  737. /**
  738. * <p>
  739. * Convenience method for registering a new listener with the specified
  740. * activation method to listen events generated by this component. If the
  741. * activation method does not have any arguments the event object will not
  742. * be passed to it when it's called.
  743. * </p>
  744. *
  745. * <p>
  746. * This version of <code>addListener</code> gets the name of the activation
  747. * method as a parameter. The actual method is reflected from
  748. * <code>object</code>, and unless exactly one match is found,
  749. * <code>java.lang.IllegalArgumentException</code> is thrown.
  750. * </p>
  751. *
  752. * <p>
  753. * For more information on the inheritable event mechanism see the
  754. * {@link com.vaadin.event com.vaadin.event package documentation}.
  755. * </p>
  756. *
  757. * <p>
  758. * Note: Using this method is discouraged because it cannot be checked
  759. * during compilation. Use {@link #addListener(Class, Object, Method)} or
  760. * {@link #addListener(com.vaadin.ui.Component.Listener)} instead.
  761. * </p>
  762. *
  763. * @param eventType
  764. * the type of the listened event. Events of this type or its
  765. * subclasses activate the listener.
  766. * @param target
  767. * the object instance who owns the activation method.
  768. * @param methodName
  769. * the name of the activation method.
  770. * @deprecated As of 7.0. This method should be avoided. Use
  771. * {@link #addListener(Class, Object, Method)} or
  772. * {@link #addListener(String, Class, Object, Method)} instead.
  773. */
  774. @Override
  775. @Deprecated
  776. public void addListener(Class<?> eventType, Object target, String methodName) {
  777. if (eventRouter == null) {
  778. eventRouter = new EventRouter();
  779. }
  780. eventRouter.addListener(eventType, target, methodName);
  781. }
  782. /**
  783. * Removes all registered listeners matching the given parameters. Since
  784. * this method receives the event type and the listener object as
  785. * parameters, it will unregister all <code>object</code>'s methods that are
  786. * registered to listen to events of type <code>eventType</code> generated
  787. * by this component.
  788. *
  789. * <p>
  790. * For more information on the inheritable event mechanism see the
  791. * {@link com.vaadin.event com.vaadin.event package documentation}.
  792. * </p>
  793. *
  794. * @param eventType
  795. * the exact event type the <code>object</code> listens to.
  796. * @param target
  797. * the target object that has registered to listen to events of
  798. * type <code>eventType</code> with one or more methods.
  799. */
  800. @Override
  801. public void removeListener(Class<?> eventType, Object target) {
  802. if (eventRouter != null) {
  803. eventRouter.removeListener(eventType, target);
  804. }
  805. }
  806. /**
  807. * Removes one registered listener method. The given method owned by the
  808. * given object will no longer be called when the specified events are
  809. * generated by this component.
  810. *
  811. * <p>
  812. * For more information on the inheritable event mechanism see the
  813. * {@link com.vaadin.event com.vaadin.event package documentation}.
  814. * </p>
  815. *
  816. * @param eventType
  817. * the exact event type the <code>object</code> listens to.
  818. * @param target
  819. * target object that has registered to listen to events of type
  820. * <code>eventType</code> with one or more methods.
  821. * @param method
  822. * the method owned by <code>target</code> that's registered to
  823. * listen to events of type <code>eventType</code>.
  824. */
  825. @Override
  826. public void removeListener(Class<?> eventType, Object target, Method method) {
  827. if (eventRouter != null) {
  828. eventRouter.removeListener(eventType, target, method);
  829. }
  830. }
  831. /**
  832. * <p>
  833. * Removes one registered listener method. The given method owned by the
  834. * given object will no longer be called when the specified events are
  835. * generated by this component.
  836. * </p>
  837. *
  838. * <p>
  839. * This version of <code>removeListener</code> gets the name of the
  840. * activation method as a parameter. The actual method is reflected from
  841. * <code>target</code>, and unless exactly one match is found,
  842. * <code>java.lang.IllegalArgumentException</code> is thrown.
  843. * </p>
  844. *
  845. * <p>
  846. * For more information on the inheritable event mechanism see the
  847. * {@link com.vaadin.event com.vaadin.event package documentation}.
  848. * </p>
  849. *
  850. * @param eventType
  851. * the exact event type the <code>object</code> listens to.
  852. * @param target
  853. * the target object that has registered to listen to events of
  854. * type <code>eventType</code> with one or more methods.
  855. * @param methodName
  856. * the name of the method owned by <code>target</code> that's
  857. * registered to listen to events of type <code>eventType</code>.
  858. * @deprecated As of 7.0. This method should be avoided. Use
  859. * {@link #removeListener(Class, Object, Method)} instead.
  860. */
  861. @Deprecated
  862. @Override
  863. public void removeListener(Class<?> eventType, Object target,
  864. String methodName) {
  865. if (eventRouter != null) {
  866. eventRouter.removeListener(eventType, target, methodName);
  867. }
  868. }
  869. /**
  870. * Returns all listeners that are registered for the given event type or one
  871. * of its subclasses.
  872. *
  873. * @param eventType
  874. * The type of event to return listeners for.
  875. * @return A collection with all registered listeners. Empty if no listeners
  876. * are found.
  877. */
  878. public Collection<?> getListeners(Class<?> eventType) {
  879. if (eventRouter == null) {
  880. return Collections.EMPTY_LIST;
  881. }
  882. return eventRouter.getListeners(eventType);
  883. }
  884. /**
  885. * Sends the event to all listeners.
  886. *
  887. * @param event
  888. * the Event to be sent to all listeners.
  889. */
  890. protected void fireEvent(EventObject event) {
  891. if (eventRouter != null) {
  892. eventRouter.fireEvent(event);
  893. }
  894. }
  895. /*
  896. * (non-Javadoc)
  897. *
  898. * @see com.vaadin.server.ClientConnector#getErrorHandler()
  899. */
  900. @Override
  901. public ErrorHandler getErrorHandler() {
  902. return errorHandler;
  903. }
  904. /*
  905. * (non-Javadoc)
  906. *
  907. * @see com.vaadin.server.ClientConnector#setErrorHandler(com.vaadin.server.
  908. * ErrorHandler)
  909. */
  910. @Override
  911. public void setErrorHandler(ErrorHandler errorHandler) {
  912. this.errorHandler = errorHandler;
  913. }
  914. }