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 33KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  1. /*
  2. * Copyright 2011 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 org.json.JSONException;
  34. import org.json.JSONObject;
  35. import com.vaadin.event.EventRouter;
  36. import com.vaadin.event.MethodEventSource;
  37. import com.vaadin.shared.communication.ClientRpc;
  38. import com.vaadin.shared.communication.ServerRpc;
  39. import com.vaadin.shared.communication.SharedState;
  40. import com.vaadin.shared.ui.ComponentStateUtil;
  41. import com.vaadin.ui.Component.Event;
  42. import com.vaadin.ui.HasComponents;
  43. import com.vaadin.ui.UI;
  44. /**
  45. * An abstract base class for ClientConnector implementations. This class
  46. * provides all the basic functionality required for connectors.
  47. *
  48. * @author Vaadin Ltd
  49. * @since 7.0.0
  50. */
  51. public abstract class AbstractClientConnector implements ClientConnector,
  52. MethodEventSource {
  53. /**
  54. * A map from client to server RPC interface class name to the RPC call
  55. * manager that handles incoming RPC calls for that interface.
  56. */
  57. private Map<String, RpcManager> rpcManagerMap = new HashMap<String, RpcManager>();
  58. /**
  59. * A map from server to client RPC interface class to the RPC proxy that
  60. * sends ourgoing RPC calls for that interface.
  61. */
  62. private Map<Class<?>, ClientRpc> rpcProxyMap = new HashMap<Class<?>, ClientRpc>();
  63. /**
  64. * Shared state object to be communicated from the server to the client when
  65. * modified.
  66. */
  67. private SharedState sharedState;
  68. private Class<? extends SharedState> stateType;
  69. /**
  70. * Pending RPC method invocations to be sent.
  71. */
  72. private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>();
  73. private String connectorId;
  74. private ArrayList<Extension> extensions = new ArrayList<Extension>();
  75. private ClientConnector parent;
  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
  103. */
  104. @Deprecated
  105. @Override
  106. public void requestRepaint() {
  107. markAsDirty();
  108. }
  109. /* Documentation copied from interface */
  110. @Override
  111. public void markAsDirty() {
  112. UI uI = getUI();
  113. if (uI != null) {
  114. uI.getConnectorTracker().markDirty(this);
  115. }
  116. }
  117. /**
  118. * Registers an RPC interface implementation for this component.
  119. *
  120. * A component can listen to multiple RPC interfaces, and subclasses can
  121. * register additional implementations.
  122. *
  123. * @since 7.0
  124. *
  125. * @param implementation
  126. * RPC interface implementation
  127. * @param rpcInterfaceType
  128. * RPC interface class for which the implementation should be
  129. * registered
  130. */
  131. protected <T extends ServerRpc> void registerRpc(T implementation,
  132. Class<T> rpcInterfaceType) {
  133. rpcManagerMap.put(rpcInterfaceType.getName(), new ServerRpcManager<T>(
  134. implementation, rpcInterfaceType));
  135. }
  136. /**
  137. * Registers an RPC interface implementation for this component.
  138. *
  139. * A component can listen to multiple RPC interfaces, and subclasses can
  140. * register additional implementations.
  141. *
  142. * @since 7.0
  143. *
  144. * @param implementation
  145. * RPC interface implementation. Also used to deduce the type.
  146. */
  147. protected <T extends ServerRpc> void registerRpc(T implementation) {
  148. Class<?> cls = implementation.getClass();
  149. Class<?>[] interfaces = cls.getInterfaces();
  150. while (interfaces.length == 0) {
  151. // Search upwards until an interface is found. It must be found as T
  152. // extends ServerRpc
  153. cls = cls.getSuperclass();
  154. interfaces = cls.getInterfaces();
  155. }
  156. if (interfaces.length != 1
  157. || !(ServerRpc.class.isAssignableFrom(interfaces[0]))) {
  158. throw new RuntimeException(
  159. "Use registerRpc(T implementation, Class<T> rpcInterfaceType) if the Rpc implementation implements more than one interface");
  160. }
  161. @SuppressWarnings("unchecked")
  162. Class<T> type = (Class<T>) interfaces[0];
  163. registerRpc(implementation, type);
  164. }
  165. /**
  166. * Returns the shared state for this connector. The shared state object is
  167. * shared between the server connector and the client connector. Changes are
  168. * only communicated from the server to the client and not in the other
  169. * direction.
  170. * <p>
  171. * As a side effect, marks the connector dirty so any changes done to the
  172. * state will be sent to the client. Use {@code getState(false)} to avoid
  173. * marking the connector as dirty.
  174. * </p>
  175. *
  176. * @return The shared state for this connector. Never null.
  177. */
  178. protected SharedState getState() {
  179. return getState(true);
  180. }
  181. /**
  182. * Returns the shared state for this connector.
  183. *
  184. * @param markAsDirty
  185. * true if the connector should automatically be marked dirty,
  186. * false otherwise
  187. *
  188. * @return The shared state for this connector. Never null.
  189. * @see #getState()
  190. */
  191. protected SharedState getState(boolean markAsDirty) {
  192. if (null == sharedState) {
  193. sharedState = createState();
  194. }
  195. if (markAsDirty) {
  196. UI ui = getUI();
  197. if (ui != null && !ui.getConnectorTracker().isWritingResponse()
  198. && !ui.getConnectorTracker().isDirty(this)) {
  199. markAsDirty();
  200. }
  201. }
  202. return sharedState;
  203. }
  204. @Override
  205. public JSONObject encodeState() throws JSONException {
  206. return AbstractCommunicationManager.encodeState(this, getState());
  207. }
  208. /**
  209. * Creates the shared state bean to be used in server to client
  210. * communication.
  211. * <p>
  212. * By default a state object of the defined return type of
  213. * {@link #getState()} is created. Subclasses can override this method and
  214. * return a new instance of the correct state class but this should rarely
  215. * be necessary.
  216. * </p>
  217. * <p>
  218. * No configuration of the values of the state should be performed in
  219. * {@link #createState()}.
  220. *
  221. * @since 7.0
  222. *
  223. * @return new shared state object
  224. */
  225. protected SharedState createState() {
  226. try {
  227. return getStateType().newInstance();
  228. } catch (Exception e) {
  229. throw new RuntimeException(
  230. "Error creating state of type " + getStateType().getName()
  231. + " for " + getClass().getName(), e);
  232. }
  233. }
  234. @Override
  235. public Class<? extends SharedState> getStateType() {
  236. // Lazy load because finding type can be expensive because of the
  237. // exceptions flying around
  238. if (stateType == null) {
  239. stateType = findStateType();
  240. }
  241. return stateType;
  242. }
  243. private Class<? extends SharedState> findStateType() {
  244. try {
  245. Class<?> class1 = getClass();
  246. while (class1 != null) {
  247. try {
  248. Method m = class1.getDeclaredMethod("getState",
  249. (Class[]) null);
  250. Class<?> type = m.getReturnType();
  251. return type.asSubclass(SharedState.class);
  252. } catch (NoSuchMethodException nsme) {
  253. // Try in superclass instead
  254. class1 = class1.getSuperclass();
  255. }
  256. }
  257. throw new NoSuchMethodException(getClass().getCanonicalName()
  258. + ".getState()");
  259. } catch (Exception e) {
  260. throw new RuntimeException("Error finding state type for "
  261. + getClass().getName(), e);
  262. }
  263. }
  264. /**
  265. * Returns an RPC proxy for a given server to client RPC interface for this
  266. * component.
  267. *
  268. * TODO more javadoc, subclasses, ...
  269. *
  270. * @param rpcInterface
  271. * RPC interface type
  272. *
  273. * @since 7.0
  274. */
  275. protected <T extends ClientRpc> T getRpcProxy(final Class<T> rpcInterface) {
  276. // create, initialize and return a dynamic proxy for RPC
  277. try {
  278. if (!rpcProxyMap.containsKey(rpcInterface)) {
  279. Class<?> proxyClass = Proxy.getProxyClass(
  280. rpcInterface.getClassLoader(), rpcInterface);
  281. Constructor<?> constructor = proxyClass
  282. .getConstructor(InvocationHandler.class);
  283. T rpcProxy = rpcInterface.cast(constructor
  284. .newInstance(new RpcInvocationHandler(rpcInterface)));
  285. // cache the proxy
  286. rpcProxyMap.put(rpcInterface, rpcProxy);
  287. }
  288. return (T) rpcProxyMap.get(rpcInterface);
  289. } catch (Exception e) {
  290. // TODO exception handling?
  291. throw new RuntimeException(e);
  292. }
  293. }
  294. private static final class AllChildrenIterable implements
  295. Iterable<ClientConnector>, Serializable {
  296. private final ClientConnector connector;
  297. private AllChildrenIterable(ClientConnector connector) {
  298. this.connector = connector;
  299. }
  300. @Override
  301. public Iterator<ClientConnector> iterator() {
  302. CombinedIterator<ClientConnector> iterator = new CombinedIterator<ClientConnector>();
  303. iterator.addIterator(connector.getExtensions().iterator());
  304. if (connector instanceof HasComponents) {
  305. HasComponents hasComponents = (HasComponents) connector;
  306. iterator.addIterator(hasComponents.iterator());
  307. }
  308. return iterator;
  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 RpcManager 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. private static final class CombinedIterator<T> implements Iterator<T>,
  426. Serializable {
  427. private final Collection<Iterator<? extends T>> iterators = new ArrayList<Iterator<? extends T>>();
  428. public void addIterator(Iterator<? extends T> iterator) {
  429. iterators.add(iterator);
  430. }
  431. @Override
  432. public boolean hasNext() {
  433. for (Iterator<? extends T> i : iterators) {
  434. if (i.hasNext()) {
  435. return true;
  436. }
  437. }
  438. return false;
  439. }
  440. @Override
  441. public T next() {
  442. for (Iterator<? extends T> i : iterators) {
  443. if (i.hasNext()) {
  444. return i.next();
  445. }
  446. }
  447. throw new NoSuchElementException();
  448. }
  449. @Override
  450. public void remove() {
  451. throw new UnsupportedOperationException();
  452. }
  453. }
  454. /**
  455. * Get an Iterable for iterating over all child connectors, including both
  456. * extensions and child components.
  457. *
  458. * @param connector
  459. * the connector to get children for
  460. * @return an Iterable giving all child connectors.
  461. */
  462. public static Iterable<ClientConnector> getAllChildrenIterable(
  463. final ClientConnector connector) {
  464. return new AllChildrenIterable(connector);
  465. }
  466. @Override
  467. public Collection<Extension> getExtensions() {
  468. return Collections.unmodifiableCollection(extensions);
  469. }
  470. /**
  471. * Add an extension to this connector. This method is protected to allow
  472. * extensions to select which targets they can extend.
  473. *
  474. * @param extension
  475. * the extension to add
  476. */
  477. protected void addExtension(Extension extension) {
  478. ClientConnector previousParent = extension.getParent();
  479. if (previousParent == this) {
  480. // Nothing to do, already attached
  481. return;
  482. } else if (previousParent != null) {
  483. throw new IllegalStateException(
  484. "Moving an extension from one parent to another is not supported");
  485. }
  486. extensions.add(extension);
  487. extension.setParent(this);
  488. markAsDirty();
  489. }
  490. @Override
  491. public void removeExtension(Extension extension) {
  492. extension.setParent(null);
  493. extensions.remove(extension);
  494. markAsDirty();
  495. }
  496. @Override
  497. public void setParent(ClientConnector parent) {
  498. // If the parent is not changed, don't do anything
  499. if (parent == this.parent) {
  500. return;
  501. }
  502. if (parent != null && this.parent != null) {
  503. throw new IllegalStateException(getClass().getName()
  504. + " already has a parent.");
  505. }
  506. // Send detach event if the component have been connected to a window
  507. if (getSession() != null) {
  508. detach();
  509. }
  510. // Connect to new parent
  511. this.parent = parent;
  512. // Send attach event if connected to an application
  513. if (getSession() != null) {
  514. attach();
  515. }
  516. }
  517. @Override
  518. public ClientConnector getParent() {
  519. return parent;
  520. }
  521. @Override
  522. public void attach() {
  523. markAsDirty();
  524. getUI().getConnectorTracker().registerConnector(this);
  525. fireEvent(new AttachEvent(this));
  526. for (ClientConnector connector : getAllChildrenIterable(this)) {
  527. connector.attach();
  528. }
  529. }
  530. /**
  531. * {@inheritDoc}
  532. *
  533. * <p>
  534. * The {@link #getSession()} and {@link #getUI()} methods might return
  535. * <code>null</code> after this method is called.
  536. * </p>
  537. */
  538. @Override
  539. public void detach() {
  540. for (ClientConnector connector : getAllChildrenIterable(this)) {
  541. connector.detach();
  542. }
  543. fireEvent(new DetachEvent(this));
  544. getUI().getConnectorTracker().unregisterConnector(this);
  545. }
  546. @Override
  547. public boolean isConnectorEnabled() {
  548. if (getParent() == null) {
  549. // No parent -> the component cannot receive updates from the client
  550. return false;
  551. } else {
  552. return getParent().isConnectorEnabled();
  553. }
  554. }
  555. @Override
  556. public void beforeClientResponse(boolean initial) {
  557. // Do nothing by default
  558. }
  559. @Override
  560. public boolean handleConnectorRequest(VaadinRequest request,
  561. VaadinResponse response, String path) throws IOException {
  562. String[] parts = path.split("/", 2);
  563. String key = parts[0];
  564. ConnectorResource resource = (ConnectorResource) getResource(key);
  565. if (resource != null) {
  566. DownloadStream stream = resource.getStream();
  567. stream.writeResponse(request, response);
  568. return true;
  569. } else {
  570. return false;
  571. }
  572. }
  573. /**
  574. * Gets a resource defined using {@link #setResource(String, Resource)} with
  575. * the corresponding key.
  576. *
  577. * @param key
  578. * the string identifier of the resource
  579. * @return a resource, or <code>null</code> if there's no resource
  580. * associated with the given key
  581. *
  582. * @see #setResource(String, Resource)
  583. */
  584. protected Resource getResource(String key) {
  585. return ResourceReference.getResource(getState().resources.get(key));
  586. }
  587. /**
  588. * Registers a resource with this connector using the given key. This will
  589. * make the URL for retrieving the resource available to the client-side
  590. * connector using
  591. * {@link com.vaadin.terminal.gwt.client.ui.AbstractConnector#getResourceUrl(String)}
  592. * with the same key.
  593. *
  594. * @param key
  595. * the string key to associate the resource with
  596. * @param resource
  597. * the resource to set, or <code>null</code> to clear a previous
  598. * association.
  599. */
  600. protected void setResource(String key, Resource resource) {
  601. ResourceReference resourceReference = ResourceReference.create(
  602. resource, this, key);
  603. if (resourceReference == null) {
  604. getState().resources.remove(key);
  605. } else {
  606. getState().resources.put(key, resourceReference);
  607. }
  608. }
  609. /* Listener code starts. Should be refactored. */
  610. /**
  611. * <p>
  612. * Registers a new listener with the specified activation method to listen
  613. * events generated by this component. If the activation method does not
  614. * have any arguments the event object will not be passed to it when it's
  615. * called.
  616. * </p>
  617. *
  618. * <p>
  619. * This method additionally informs the event-api to route events with the
  620. * given eventIdentifier to the components handleEvent function call.
  621. * </p>
  622. *
  623. * <p>
  624. * For more information on the inheritable event mechanism see the
  625. * {@link com.vaadin.event com.vaadin.event package documentation}.
  626. * </p>
  627. *
  628. * @param eventIdentifier
  629. * the identifier of the event to listen for
  630. * @param eventType
  631. * the type of the listened event. Events of this type or its
  632. * subclasses activate the listener.
  633. * @param target
  634. * the object instance who owns the activation method.
  635. * @param method
  636. * the activation method.
  637. *
  638. * @since 6.2
  639. */
  640. protected void addListener(String eventIdentifier, Class<?> eventType,
  641. Object target, Method method) {
  642. if (eventRouter == null) {
  643. eventRouter = new EventRouter();
  644. }
  645. boolean needRepaint = !eventRouter.hasListeners(eventType);
  646. eventRouter.addListener(eventType, target, method);
  647. if (needRepaint) {
  648. ComponentStateUtil.addRegisteredEventListener(getState(),
  649. eventIdentifier);
  650. }
  651. }
  652. /**
  653. * Checks if the given {@link Event} type is listened for this component.
  654. *
  655. * @param eventType
  656. * the event type to be checked
  657. * @return true if a listener is registered for the given event type
  658. */
  659. protected boolean hasListeners(Class<?> eventType) {
  660. return eventRouter != null && eventRouter.hasListeners(eventType);
  661. }
  662. /**
  663. * Removes all registered listeners matching the given parameters. Since
  664. * this method receives the event type and the listener object as
  665. * parameters, it will unregister all <code>object</code>'s methods that are
  666. * registered to listen to events of type <code>eventType</code> generated
  667. * by this component.
  668. *
  669. * <p>
  670. * This method additionally informs the event-api to stop routing events
  671. * with the given eventIdentifier to the components handleEvent function
  672. * call.
  673. * </p>
  674. *
  675. * <p>
  676. * For more information on the inheritable event mechanism see the
  677. * {@link com.vaadin.event com.vaadin.event package documentation}.
  678. * </p>
  679. *
  680. * @param eventIdentifier
  681. * the identifier of the event to stop listening for
  682. * @param eventType
  683. * the exact event type the <code>object</code> listens to.
  684. * @param target
  685. * the target object that has registered to listen to events of
  686. * type <code>eventType</code> with one or more methods.
  687. *
  688. * @since 6.2
  689. */
  690. protected void removeListener(String eventIdentifier, Class<?> eventType,
  691. Object target) {
  692. if (eventRouter != null) {
  693. eventRouter.removeListener(eventType, target);
  694. if (!eventRouter.hasListeners(eventType)) {
  695. ComponentStateUtil.removeRegisteredEventListener(getState(),
  696. eventIdentifier);
  697. }
  698. }
  699. }
  700. /**
  701. * <p>
  702. * Registers a new listener with the specified activation method to listen
  703. * events generated by this component. If the activation method does not
  704. * have any arguments the event object will not be passed to it when it's
  705. * called.
  706. * </p>
  707. *
  708. * <p>
  709. * For more information on the inheritable event mechanism see the
  710. * {@link com.vaadin.event com.vaadin.event package documentation}.
  711. * </p>
  712. *
  713. * @param eventType
  714. * the type of the listened event. Events of this type or its
  715. * subclasses activate the listener.
  716. * @param target
  717. * the object instance who owns the activation method.
  718. * @param method
  719. * the activation method.
  720. *
  721. */
  722. @Override
  723. public void addListener(Class<?> eventType, Object target, Method method) {
  724. if (eventRouter == null) {
  725. eventRouter = new EventRouter();
  726. }
  727. eventRouter.addListener(eventType, target, method);
  728. }
  729. /**
  730. * <p>
  731. * Convenience method for registering a new listener with the specified
  732. * activation method to listen events generated by this component. If the
  733. * activation method does not have any arguments the event object will not
  734. * be passed to it when it's called.
  735. * </p>
  736. *
  737. * <p>
  738. * This version of <code>addListener</code> gets the name of the activation
  739. * method as a parameter. The actual method is reflected from
  740. * <code>object</code>, and unless exactly one match is found,
  741. * <code>java.lang.IllegalArgumentException</code> is thrown.
  742. * </p>
  743. *
  744. * <p>
  745. * For more information on the inheritable event mechanism see the
  746. * {@link com.vaadin.event com.vaadin.event package documentation}.
  747. * </p>
  748. *
  749. * <p>
  750. * Note: Using this method is discouraged because it cannot be checked
  751. * during compilation. Use {@link #addListener(Class, Object, Method)} or
  752. * {@link #addListener(com.vaadin.ui.Component.Listener)} instead.
  753. * </p>
  754. *
  755. * @param eventType
  756. * the type of the listened event. Events of this type or its
  757. * subclasses activate the listener.
  758. * @param target
  759. * the object instance who owns the activation method.
  760. * @param methodName
  761. * the name of the activation method.
  762. * @deprecated As of 7.0. This method should be avoided. Use
  763. * {@link #addListener(Class, Object, Method)} or
  764. * {@link #addListener(String, Class, Object, Method)} instead.
  765. */
  766. @Override
  767. @Deprecated
  768. public void addListener(Class<?> eventType, Object target, String methodName) {
  769. if (eventRouter == null) {
  770. eventRouter = new EventRouter();
  771. }
  772. eventRouter.addListener(eventType, target, methodName);
  773. }
  774. /**
  775. * Removes all registered listeners matching the given parameters. Since
  776. * this method receives the event type and the listener object as
  777. * parameters, it will unregister all <code>object</code>'s methods that are
  778. * registered to listen to events of type <code>eventType</code> generated
  779. * by this component.
  780. *
  781. * <p>
  782. * For more information on the inheritable event mechanism see the
  783. * {@link com.vaadin.event com.vaadin.event package documentation}.
  784. * </p>
  785. *
  786. * @param eventType
  787. * the exact event type the <code>object</code> listens to.
  788. * @param target
  789. * the target object that has registered to listen to events of
  790. * type <code>eventType</code> with one or more methods.
  791. */
  792. @Override
  793. public void removeListener(Class<?> eventType, Object target) {
  794. if (eventRouter != null) {
  795. eventRouter.removeListener(eventType, target);
  796. }
  797. }
  798. /**
  799. * Removes one registered listener method. The given method owned by the
  800. * given object will no longer be called when the specified events are
  801. * generated by this component.
  802. *
  803. * <p>
  804. * For more information on the inheritable event mechanism see the
  805. * {@link com.vaadin.event com.vaadin.event package documentation}.
  806. * </p>
  807. *
  808. * @param eventType
  809. * the exact event type the <code>object</code> listens to.
  810. * @param target
  811. * target object that has registered to listen to events of type
  812. * <code>eventType</code> with one or more methods.
  813. * @param method
  814. * the method owned by <code>target</code> that's registered to
  815. * listen to events of type <code>eventType</code>.
  816. */
  817. @Override
  818. public void removeListener(Class<?> eventType, Object target, Method method) {
  819. if (eventRouter != null) {
  820. eventRouter.removeListener(eventType, target, method);
  821. }
  822. }
  823. /**
  824. * <p>
  825. * Removes one registered listener method. The given method owned by the
  826. * given object will no longer be called when the specified events are
  827. * generated by this component.
  828. * </p>
  829. *
  830. * <p>
  831. * This version of <code>removeListener</code> gets the name of the
  832. * activation method as a parameter. The actual method is reflected from
  833. * <code>target</code>, and unless exactly one match is found,
  834. * <code>java.lang.IllegalArgumentException</code> is thrown.
  835. * </p>
  836. *
  837. * <p>
  838. * For more information on the inheritable event mechanism see the
  839. * {@link com.vaadin.event com.vaadin.event package documentation}.
  840. * </p>
  841. *
  842. * @param eventType
  843. * the exact event type the <code>object</code> listens to.
  844. * @param target
  845. * the target object that has registered to listen to events of
  846. * type <code>eventType</code> with one or more methods.
  847. * @param methodName
  848. * the name of the method owned by <code>target</code> that's
  849. * registered to listen to events of type <code>eventType</code>.
  850. * @deprecated As of 7.0. This method should be avoided. Use
  851. * {@link #removeListener(Class, Object, Method)} instead.
  852. */
  853. @Deprecated
  854. @Override
  855. public void removeListener(Class<?> eventType, Object target,
  856. String methodName) {
  857. if (eventRouter != null) {
  858. eventRouter.removeListener(eventType, target, methodName);
  859. }
  860. }
  861. /**
  862. * Returns all listeners that are registered for the given event type or one
  863. * of its subclasses.
  864. *
  865. * @param eventType
  866. * The type of event to return listeners for.
  867. * @return A collection with all registered listeners. Empty if no listeners
  868. * are found.
  869. */
  870. public Collection<?> getListeners(Class<?> eventType) {
  871. if (eventRouter == null) {
  872. return Collections.EMPTY_LIST;
  873. }
  874. return eventRouter.getListeners(eventType);
  875. }
  876. /**
  877. * Sends the event to all listeners.
  878. *
  879. * @param event
  880. * the Event to be sent to all listeners.
  881. */
  882. protected void fireEvent(EventObject event) {
  883. if (eventRouter != null) {
  884. eventRouter.fireEvent(event);
  885. }
  886. }
  887. /*
  888. * (non-Javadoc)
  889. *
  890. * @see com.vaadin.server.ClientConnector#getErrorHandler()
  891. */
  892. public ErrorHandler getErrorHandler() {
  893. return errorHandler;
  894. }
  895. /*
  896. * (non-Javadoc)
  897. *
  898. * @see com.vaadin.server.ClientConnector#setErrorHandler(com.vaadin.server.
  899. * ErrorHandler)
  900. */
  901. public void setErrorHandler(ErrorHandler errorHandler) {
  902. this.errorHandler = errorHandler;
  903. }
  904. }