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

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