/**
   * Provide an instance of {@link javax.websocket.Encoder} or {@link javax.websocket.Decoder}
   * descendant which is coupled to {@link Session}. The first time the method is called the
   * provider creates an instance, calls {@link
   * javax.websocket.Encoder#init(javax.websocket.EndpointConfig)} or {@link
   * javax.websocket.Decoder#init(javax.websocket.EndpointConfig)} and caches it. Next time the
   * method is called the cached instance is returned.
   *
   * @param c {@link Class} whose instance will be provided.
   * @param collector error collector.
   * @param endpointConfig configuration corresponding to current context. Used for {@link
   *     javax.websocket.Encoder#init(javax.websocket.EndpointConfig)} and {@link
   *     javax.websocket.Decoder#init(javax.websocket.EndpointConfig)}
   * @param <T> type of the provided instance.
   * @return instance
   */
  public <T> Object getCoderInstance(
      Class<T> c, Session session, EndpointConfig endpointConfig, ErrorCollector collector) {
    Object loaded = null;

    final Map<Class<?>, Object> classObjectMap = sessionToObject.get(session);

    try {
      if (classObjectMap != null) {
        synchronized (classObjectMap) {
          if (classObjectMap.containsKey(c)) {
            loaded = classObjectMap.get(c);
          } else {
            loaded = getInstance(c);
            if (loaded != null) {
              if (loaded instanceof Encoder) {
                ((Encoder) loaded).init(endpointConfig);
              } else if (loaded instanceof Decoder) {
                ((Decoder) loaded).init(endpointConfig);
              }
              sessionToObject.get(session).put(c, loaded);
            }
          }
        }
      } else {
        loaded = getInstance(c);
        if (loaded != null) {
          if (loaded instanceof Encoder) {
            ((Encoder) loaded).init(endpointConfig);
          } else if (loaded instanceof Decoder) {
            ((Decoder) loaded).init(endpointConfig);
          }
          final HashMap<Class<?>, Object> hashMap = new HashMap<Class<?>, Object>();
          hashMap.put(c, loaded);

          sessionToObject.put(session, hashMap);
        }
      }
    } catch (InstantiationException e) {
      collector.addException(
          new DeploymentException(
              LocalizationMessages.COMPONENT_PROVIDER_THREW_EXCEPTION(c.getName()), e));
    }

    return loaded;
  }
  /**
   * Remove {@link Session} from cache.
   *
   * @param session to be removed.
   */
  public void removeSession(Session session) {
    final Map<Class<?>, Object> classObjectMap = sessionToObject.get(session);
    if (classObjectMap != null) {
      synchronized (classObjectMap) {
        for (Object o : classObjectMap.values()) {
          if (o instanceof Encoder) {
            ((Encoder) o).destroy();
          } else if (o instanceof Decoder) {
            ((Decoder) o).destroy();
          }

          for (ComponentProvider componentProvider : providers) {
            if (componentProvider.destroy(o)) {
              break;
            }
          }
        }
      }
    }

    sessionToObject.remove(session);
  }