EHCACHE 3.x spring boot no of value in cache - spring-boot

I am trying to use ehcache.
#Configuration
#EnableCaching
public class CacheConfig {
#Bean
public JCacheCacheManager jCacheCacheManager() throws IOException {
return new JCacheCacheManager(cacheManager());
}
#Bean(destroyMethod = "close")
public javax.cache.CacheManager cacheManager() throws IOException {
XmlConfiguration xmlConfig = new XmlConfiguration(new ClassPathResource("ehcache.xml").getURL());
EhcacheCachingProvider provider = (EhcacheCachingProvider) Caching.getCachingProvider();
return provider.getCacheManager(provider.getDefaultURI(), xmlConfig);
}
}
#Service
public class AvengersService {
#Cacheable(cacheNames="avengers" , key="#id")
public List<String> getAvengers(String id) {
System.out.println("Loading avengers");
return Arrays.asList(id.toString(),"Captain America", "Hulk", "Iron Man", "Thor");
}
}
#SpringBootApplication
#ComponentScan
public class DemoApplication {
#Autowired
private CacheManager cacheManager;
#Autowired
private AvengersService avengersService;
public static void main(String[] args) {
//list all the caching provider
Iterator<CachingProvider> iterator = Caching.getCachingProviders(Caching.getDefaultClassLoader()).iterator();
while(iterator.hasNext()) {
CachingProvider provider = iterator.next();
System.out.println(provider);
if ((provider instanceof HazelcastCachingProvider)) {
iterator.remove();
}
}
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
JCacheCache cache = (JCacheCache) cacheManager
.getCache("avengers");
for(Long i=1L;i<50001;i++) {
List<String> t=avengersService.getAvengers(String.valueOf(i)+"a");
cache.put(String.valueOf(i)+"a", avengersService.getAvengers(String.valueOf(i)+"a"));
}
System.out.println();
Cache<Object, Object> e= ( cache.getNativeCache());
Iterator<Cache.Entry<Object, Object>> iterator = ( e).iterator();
int count=1;
while (iterator.hasNext()) {
Cache.Entry<Object, Object> t = iterator.next();
count++;
System.out.println("key :" + t.getKey() + " value " + t.getValue());
}
System.out.println(count);
final MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
final Set<ObjectInstance> cacheBeans = beanServer.queryMBeans(ObjectName.getInstance("javax.cache:type=CacheStatistics,CacheManager=*,Cache=*"), null);
for ( ObjectInstance cacheBean : cacheBeans ) {
final CacheStatisticsMXBean cacheStatisticsMXBean = MBeanServerInvocationHandler.newProxyInstance(beanServer, cacheBean.getObjectName(), CacheStatisticsMXBean.class, false);
System.out.println("Gets: " + cacheStatisticsMXBean.getCacheGets());
System.out.println("Hits: " + cacheStatisticsMXBean.getCacheHits());
System.out.println("Misses: " + cacheStatisticsMXBean.getCacheMisses());
System.out.println("Removals: " + cacheStatisticsMXBean.getCacheRemovals());
System.out.println("Evictions: " + cacheStatisticsMXBean.getCacheEvictions());
System.out.println("Avg Get Time: " + cacheStatisticsMXBean.getAverageGetTime());
System.out.println("Avg Put Time: " + cacheStatisticsMXBean.getAveragePutTime());
System.out.println("Avg Remove Time: " + cacheStatisticsMXBean.getAverageRemoveTime());
}
};
}
}
public class MyCacheEntryListener<K, V> implements CacheEventListener<K, V>, Serializable {
#Override
public void onEvent(CacheEvent<? extends K, ? extends V> event) {
// TODO Auto-generated method stub
if (EventType.UPDATED == event.getType()) {
System.out.println("Received a " + event);
}
}
}
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<service>
<jsr107:defaults enable-management="true" enable-statistics="true"/>
</service>
<cache alias="avengers">
<key-type>java.lang.String</key-type>
<value-type>java.util.Arrays$ArrayList</value-type>
<expiry>
<none/>
</expiry>
<listeners>
<listener>
<class>com.example.demo.MyCacheEntryListener</class>
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
<event-ordering-mode>UNORDERED</event-ordering-mode>
<events-to-fire-on>UPDATED</events-to-fire-on>
</listener>
</listeners>
<resources>
<heap unit="entries">2</heap>
<offheap unit="MB">10</offheap>
</resources>
</cache>
</config>
spring.cache.jcache.config=classpath:ehcache.xml
Blockquote
While starting my appilication its prints 2 cache provider.
HazelcastCachingProvider{delegate=HazelcastServerCachingProvider{hazelcastInstance=null}}
org.ehcache.jsr107.EhcacheCachingProvider#1f57539
Why its initialising the 2 cache provider?
if its default how can I disable these?
I have added 50000 object in the cache but while iterating it i am only getting "36406" object why?

There are a lot of moving parts here. You don't need to configure the CacheManager, it is done automatically with the spring.cache.jcache.config property. Remove the 2 #Bean methods.
If you need to access the javax.cache.CacheManager, you can inject JCacheCacheManager and it'll give you access to the native one it wraps.
There are several JSR107 implementations on the classpath so that API call gives you what's available, this is nothing Spring Boot has control over.

Related

Howto implement Keycloak `AbstractOIDCProtocolMapper` with spring DI?

I have implemented a Keycloak mapper and it works but without security.
Now, I'd like to use spring-boot to use dependency injection and make it an OAuth2 client (use spring-security and spring-boot properties to authenticate using OAuth2 client-crediential flow).
Here is what I did so far
maven project with maven shade plugin
META-INF/jboss-deployment-structure.xml
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="org.keycloak.keycloak-services" />
</dependencies>
</deployment>
</jboss-deployment-structure>
META-INF/services/org.keycloak.protocol.ProtocolMapper
com.c4_soft.ProxiesMapper
extend AbstractOIDCProtocolMapper and implement OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper
public class ProxiesMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
private static final String PROVIDER_ID = "c4-soft.com";
private static final String PROXIES_SERVICE_BASE_URI = "proxies-service.base-uri";
private static Logger logger = Logger.getLogger(ProxiesMapper.class);
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(PROXIES_SERVICE_BASE_URI);
property.setLabel("Proxies service base URI");
property.setHelpText("Base URI for REST service to fetch proxies from");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setDefaultValue("https://localhost:4204");
configProperties.add(property);
}
private final Map<String, WebClient> webClientByBaseUri = new HashMap<>();
#Override
public String getDisplayCategory() {
return TOKEN_MAPPER_CATEGORY;
}
#Override
public String getDisplayType() {
return "User proxies mapper";
}
#Override
public String getId() {
return PROVIDER_ID;
}
#Override
public String getHelpText() {
return "Adds a \"proxies\" private claim containing a map of authorizations the user has to act on behalf of other users (one collection of grant IDs per user subject)";
}
#Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
#Override
public AccessToken transformAccessToken(
AccessToken token,
ProtocolMapperModel mappingModel,
KeycloakSession keycloakSession,
UserSessionModel userSession,
ClientSessionContext clientSessionCtx) {
Map<String, Collection<String>> grantsByProxiedUserSubject;
try {
#SuppressWarnings("unchecked")
final List<ProxyDto> dtos =
getWebClient(mappingModel).get().uri("/users/{userSubject}/proxies/granted", token.getSubject()).retrieve().bodyToMono(List.class).block();
grantsByProxiedUserSubject = dtos.stream().collect(Collectors.toMap(ProxyDto::getGrantingUserSubject, ProxyDto::getGrants));
} catch (final WebClientResponseException e) {
grantsByProxiedUserSubject = null;
logger.warn("Failed to fetch user proxies", e);
}
token.getOtherClaims().put("proxies", grantsByProxiedUserSubject);
setClaim(token, mappingModel, userSession, keycloakSession, clientSessionCtx);
return token;
}
public static ProtocolMapperModel create() {
final var mapper = new ProtocolMapperModel();
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
final Map<String, String> config = new HashMap<>();
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
mapper.setConfig(config);
return mapper;
}
private WebClient getWebClient(ProtocolMapperModel mappingModel) {
final var baseUri = mappingModel.getConfig().get(PROXIES_SERVICE_BASE_URI);
return webClientByBaseUri.computeIfAbsent(baseUri, (String k) -> WebClient.builder().baseUrl(baseUri).build());
}
}

Spring Boot Kafka Configure DefaultErrorHandler?

I created a batch-consumer following the Spring Kafka docs:
#SpringBootApplication
public class ApplicationConsumer {
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationConsumer.class);
private static final String TOPIC = "foo";
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ApplicationConsumer.class, args);
}
#Bean
public RecordMessageConverter converter() {
return new JsonMessageConverter();
}
#Bean
public BatchMessagingMessageConverter batchConverter() {
return new BatchMessagingMessageConverter(converter());
}
#KafkaListener(topics = TOPIC)
public void listen(List<Name> ps) {
LOGGER.info("received name beans: {}", Arrays.toString(ps.toArray()));
}
}
I was able to successfully get the consumer running by defining the following additional configuration env variables, that Spring automatically picks up:
export SPRING_KAFKA_BOOTSTRAP-SERVERS=...
export SPRING_KAFKA_CONSUMER_GROUP-ID=...
So the above code works. But now I want to customize the default error handler to use exponential backoff. From the ref docs I tried adding the following to ApplicationConsumer class:
#Bean
public ConcurrentKafkaListenerContainerFactory<?, ?> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setCommonErrorHandler(new DefaultErrorHandler(new ExponentialBackOffWithMaxRetries(10)));
factory.setConsumerFactory(consumerFactory());
return factory;
}
#Bean
public ConsumerFactory<String, Object> consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerConfigs());
}
#Bean
public Map<String, Object> consumerConfigs() {
Map<String, Object> props = new HashMap<>();
return props;
}
But now I get errors saying that it can't find some of the configuration. It looks like I'm stuck having to redefine all of the properties in consumerConfigs() that were already being automatically defined before. This includes everything from bootstrap server uris to the json-deserialization config.
Is there a good way to update my first version of the code to just override the default-error handler?
Just define the error handler as a #Bean and Boot will automatically wire it into its auto configured container factory.
EDIT
This works as expected for me:
#SpringBootApplication
public class So70884203Application {
public static void main(String[] args) {
SpringApplication.run(So70884203Application.class, args);
}
#Bean
DefaultErrorHandler eh() {
return new DefaultErrorHandler((rec, ex) -> {
System.out.println("Recovered: " + rec);
}, new FixedBackOff(0L, 0L));
}
#KafkaListener(id = "so70884203", topics = "so70884203")
void listen(String in) {
System.out.println(in);
throw new RuntimeException("test");
}
#Bean
NewTopic topic() {
return TopicBuilder.name("so70884203").partitions(1).replicas(1).build();
}
}
foo
Recovered: ConsumerRecord(topic = so70884203, partition = 0, leaderEpoch = 0, offset = 0, CreateTime = 1643316625291, serialized key size = -1, serialized value size = 3, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = foo)

Spring-boot 2 rabbitmq MessageConverter not working as excepted

I am using the springboot and rabitmq. I have kind of similar scenario here.
I would like to map my message to custom java object
I also want pass delivery tag which is message proprieties
I wann to pass Channel as well beacuse I need to manual ack messages
My code is like this
#RunWith(SpringRunner.class)
public class EPPQ2SubscriberTest {
#Autowired
private RabbitListenerEndpointRegistry registry;
public final String sampleMessage = "{" + "\"header\": {" + "\"RETRY_COUNT\":0," + "\"PUBLISH_EVENT_TYPE\":\"AUTH\""
+ "}," + "\"payLoad\":{" + "\"MTI\": \"120\"," + "\"MTI_REQUEST\": \"120\","
+ "\"PAN\": \"6011000000000000\"" + "}" + "}";
#Test
public void message_converter_test() throws Exception {
SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) this.registry
.getListenerContainer("messageListener");
ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener();
Message message = MessageBuilder.withBody(sampleMessage.getBytes())
.andProperties(MessagePropertiesBuilder.newInstance().setContentType("application/json").build())
.build();
listener.onMessage(message, mock(Channel.class));
}
#Configuration
#EnableRabbit
public static class config {
#Bean
public ConnectionFactory mockConnectionFactory() {
return mock(ConnectionFactory.class);
}
#Bean
public MessageConverter messageConverter() {
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
/*
* DefaultClassMapper classMapper = new DefaultClassMapper();
* classMapper.setDefaultType(com.discover.dftp.scrubber.domain.Message.class);
* messageConverter.setClassMapper(classMapper);
*/
return messageConverter;
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(mockConnectionFactory());
factory.setMessageConverter(messageConverter());
factory.setAutoStartup(false);
return factory;
}
#Bean
public EPPQ2Subscriber messageListener() {
return new EPPQ2Subscriber();
}
}
}
#Component
public class EPPQ2Subscriber {
private static final Logger LOGGER = LoggerFactory.getLogger(EPPQ2Subscriber.class);
// #RabbitListener(queues = "#{queue.getName()}") #TODO I wann to use this in
// later point in time.. !
#RabbitListener(id = "messageListener", queues = "TestQueue")
public void receiveMessage(Message message, Channel channel/* ,#Header(AmqpHeaders.DELIVERY_TAG) long tag */) {
LOGGER.info("Method receiveMessage invoked");
message.getMessageProperties().getDeliveryTag();
LOGGER.info("Result:" + message.getClass() + ":" + message.toString());
}
}
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, Object> header;
private Map<String, Object> payLoad;
public Map<String, Object> getHeader() {
return header;
}
public void setHeader(Map<String, Object> header) {
this.header = header;
}
public Map<String, Object> getPayLoad() {
return payLoad;
}
public void setPayLoad(Map<String, Object> payLoad) {
this.payLoad = payLoad;
}
#Override
public String toString() {
return "Header [header=" + this.header + ", payLoad=" + this.payLoad + "]";
}
}
#RabbitListener(id = "messageListener", queues = "TestQueue")
public void receiveMessage(Message message, Channel channel/* ,#Header(AmqpHeaders.DELIVERY_TAG) long tag */) {
LOGGER.info("Method receiveMessage invoked");
message.getMessageProperties().getDeliveryTag();
LOGGER.info("Result:" + message.getClass() + ":" + message.toString());
}
I looks like that method receives the raw (unconverted) Spring AMQP Message (you are importing the wrong Message class in EPPQ2Subscriber).

Spring Application and Hibernate Application without XML configuration

Recently in a interview, It was asked from me that is it possible to have Spring Application or Hibernate Application without XML configuration..?
So what could be the best answer of it, Please explain.
Yes you can, e.g:-> Account
#Entity
public class Account {
#Id
#GeneratedValue
private Long id;
#Temporal(TemporalType.DATE)
private Data data;
private Long accountNumber;
private String owner;
private double balance;
public Long getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(Long accountNumber) {
this.accountNumber = accountNumber;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
#Override
public String toString() {
return "Account{" +
"accountNumber=" + accountNumber +
", owner='" + owner + '\'' +
", balance=" + balance +
'}';
}
}
Dao:
#Repository
public class AccountDao {
#PersistenceContext
EntityManager entityManager;
#Transactional
public void save(Account account) {
entityManager.persist(account);
}
protected Account getByKey(Long key) {
return (Account) entityManager.find(Account.class, key);
}
}
Configuration->
#Configuration
#EnableTransactionManagement
#ComponentScan("your scan package path")
public class AppConfig {
#Bean
public DataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setUsername("root");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/your_data_base_name");
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
driverManagerDataSource.setPassword("password");
return driverManagerDataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
localContainerEntityManagerFactoryBean.setDataSource(dataSource());
localContainerEntityManagerFactoryBean.setJpaVendorAdapter(getHibernateJpaVendorAdapter());
localContainerEntityManagerFactoryBean.setJpaProperties(additionalProperties());
localContainerEntityManagerFactoryBean.setPackagesToScan("entity_package_name");
return localContainerEntityManagerFactoryBean;
}
#Bean
public JpaVendorAdapter getHibernateJpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(entityManagerFactory().getObject());
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.hbm2ddl.auto", "create");
properties.put("hibernate_show_sql", true);
return properties;
}
}
Test class :
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Account account = new Account();
account.setAccountNumber(123L);
account.setBalance(100056.5);
AccountService accountService = applicationContext.getBean(AccountService.class);
accountService.save(account);
}
}

Spring MVC with Atmosphere

I have recently started with Atmosphere. I need it to implement it in a Spring MVC application.
Till now I've managed to integrate it with Spring MVC.
I just need to perform a very simple task. I have a counter an instance variable as soon as it reaches 10, a response should be broadcasted to the UI.
Can anyone help me how do I write the code for that in the controller.
I've got the Atmosphere resource into the controller.
AtmosphereArgumentResolver.java
public class AtmosphereArgumentResolver implements HandlerMethodArgumentResolver {
//#Override
public boolean supportsParameter(MethodParameter parameter) {
return AtmosphereResource.class.isAssignableFrom(parameter.getParameterType());
}
//#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception
{
HttpServletRequest httpServletRequest= webRequest.getNativeRequest(HttpServletRequest.class);
return Meteor.build(httpServletRequest).getAtmosphereResource();
}
}
HomeController.java
#Controller
public class HomeController {
private int counter = 0;
private final BroadcasterFactory bf;
public BroadcasterFactory broadcasterFactory()
{
return BroadcasterFactory.getDefault();
}
for(int i=0; i<=15; i++)
{
counter ++;
}
// As soon as the counter reaches 10 I need to send a broadcast message to the UI.
}
Can anyone please help? A skeleton code would also help as in which Atmosphere method to use for this?
I will copy/past the code i use in my application :
Controller :
#ManagedService(path = "/websocket/*")
#Singleton
public class LanesWebSocket {
private final Logger logger = LoggerFactory.getLogger(LanesWebSocket.class);
// private ScheduledExecutorService scheduledExecutorService;
private Future<?> scheduleFixedBroadcast;
private final ObjectMapper mapper = new ObjectMapper();
private SupervisionCenterService supervisionCenterService;
#Ready
public void onReady(final AtmosphereResource resource) {
if (this.supervisionCenterService == null)
supervisionCenterService = SpringApplicationContext.getBean(SupervisionCenterService.class);
Broadcaster bc = BroadcasterFactory.getDefault().lookup("lanes",true);
bc.addAtmosphereResource(resource);
scheduleFixedBroadcast = bc.scheduleFixedBroadcast(new Callable<String>() {
#Override
public String call() throws Exception {
try {
return mapper.writeValueAsString(supervisionCenterService.findCenterData());
} catch (Exception e) {
scheduleFixedBroadcast.cancel(true);
e.printStackTrace();
return null;
}
}
}, 1, TimeUnit.SECONDS);
}
And you also need to register the atmosphere servlet :
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
[...]
#Override
protected void registerDispatcherServlet(ServletContext servletContext) {
super.registerDispatcherServlet(servletContext);
initAtmosphereServlet(servletContext);
}
private void initAtmosphereServlet(ServletContext servletContext) {
AtmosphereServlet servlet = new AtmosphereServlet();
Field frameworkField = ReflectionUtils.findField(AtmosphereServlet.class, "framework");
ReflectionUtils.makeAccessible(frameworkField);
ReflectionUtils.setField(frameworkField, servlet, new NoAnalyticsAtmosphereFramework());
ServletRegistration.Dynamic atmosphereServlet =
servletContext.addServlet("atmosphereServlet", servlet);
atmosphereServlet.setInitParameter("org.atmosphere.cpr.packages", "com.myclient.theproduct.supervision.websocket");
atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcasterCacheClass", UUIDBroadcasterCache.class.getName());
atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.shareableThreadPool", "true");
atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.maxProcessingThreads", "10");
atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.maxAsyncWriteThreads", "10");
servletContext.addListener(new org.atmosphere.cpr.SessionSupport());
atmosphereServlet.addMapping("/websocket/*");
atmosphereServlet.setLoadOnStartup(3);
atmosphereServlet.setAsyncSupported(true);
}
public class NoAnalyticsAtmosphereFramework extends AtmosphereFramework {
public NoAnalyticsAtmosphereFramework() {
super();
}
#Override
protected void analytics() {
// nothing
}
}
}
Don't ask me the reason of the NoAnalyticsAtmosphereFramework class, it could not work without.
Hope this will help you !

Resources