com.hazelcast.cache.impl.CacheEventListenerAdaptor Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2018, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.cache.impl;
import com.hazelcast.cache.CacheEventType;
import com.hazelcast.cache.ICache;
import com.hazelcast.core.ManagedContext;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.nio.serialization.IdentifiedDataSerializable;
import com.hazelcast.spi.EventRegistration;
import com.hazelcast.spi.ListenerWrapperEventFilter;
import com.hazelcast.spi.NotifiableEventListener;
import com.hazelcast.spi.serialization.SerializationService;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.Factory;
import javax.cache.event.CacheEntryCreatedListener;
import javax.cache.event.CacheEntryEvent;
import javax.cache.event.CacheEntryEventFilter;
import javax.cache.event.CacheEntryExpiredListener;
import javax.cache.event.CacheEntryListener;
import javax.cache.event.CacheEntryRemovedListener;
import javax.cache.event.CacheEntryUpdatedListener;
import javax.cache.event.EventType;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
/**
* This implementation of {@link CacheEventListener} uses the adapter pattern for wrapping all cache event listener
* types into a single listener.
* JCache has multiple {@link CacheEntryListener} sub-interfaces for each event type. This adapter
* implementation delegates to the correct subtype using the event type.
*
* Another responsibility of this implementation is filtering events by using the already configured
* event filters.
*
* @param the type of key.
* @param the type of value.
* @see javax.cache.event.CacheEntryCreatedListener
* @see javax.cache.event.CacheEntryUpdatedListener
* @see javax.cache.event.CacheEntryRemovedListener
* @see javax.cache.event.CacheEntryExpiredListener
* @see javax.cache.event.CacheEntryEventFilter
*/
public class CacheEventListenerAdaptor
implements CacheEventListener,
CacheEntryListenerProvider,
NotifiableEventListener,
ListenerWrapperEventFilter,
IdentifiedDataSerializable {
// all fields are effectively final
private transient CacheEntryListener cacheEntryListener;
private transient CacheEntryCreatedListener cacheEntryCreatedListener;
private transient CacheEntryRemovedListener cacheEntryRemovedListener;
private transient CacheEntryUpdatedListener cacheEntryUpdatedListener;
private transient CacheEntryExpiredListener cacheEntryExpiredListener;
private transient CacheEntryEventFilter filter;
private boolean isOldValueRequired;
private transient SerializationService serializationService;
private transient ICache source;
public CacheEventListenerAdaptor() {
}
public CacheEventListenerAdaptor(ICache source,
CacheEntryListenerConfiguration cacheEntryListenerConfiguration,
SerializationService serializationService) {
this.source = source;
this.serializationService = serializationService;
this.cacheEntryListener = createCacheEntryListener(cacheEntryListenerConfiguration);
if (cacheEntryListener instanceof CacheEntryCreatedListener) {
this.cacheEntryCreatedListener = (CacheEntryCreatedListener) cacheEntryListener;
} else {
this.cacheEntryCreatedListener = null;
}
if (cacheEntryListener instanceof CacheEntryRemovedListener) {
this.cacheEntryRemovedListener = (CacheEntryRemovedListener) cacheEntryListener;
} else {
this.cacheEntryRemovedListener = null;
}
if (cacheEntryListener instanceof CacheEntryUpdatedListener) {
this.cacheEntryUpdatedListener = (CacheEntryUpdatedListener) cacheEntryListener;
} else {
this.cacheEntryUpdatedListener = null;
}
if (cacheEntryListener instanceof CacheEntryExpiredListener) {
this.cacheEntryExpiredListener = (CacheEntryExpiredListener) cacheEntryListener;
} else {
this.cacheEntryExpiredListener = null;
}
injectDependencies(cacheEntryListener);
Factory> filterFactory =
cacheEntryListenerConfiguration.getCacheEntryEventFilterFactory();
if (filterFactory != null) {
this.filter = filterFactory.create();
} else {
this.filter = null;
}
injectDependencies(filter);
this.isOldValueRequired = cacheEntryListenerConfiguration.isOldValueRequired();
}
private CacheEntryListener createCacheEntryListener(
CacheEntryListenerConfiguration cacheEntryListenerConfiguration) {
Factory> cacheEntryListenerFactory =
cacheEntryListenerConfiguration.getCacheEntryListenerFactory();
injectDependencies(cacheEntryListenerFactory);
return (CacheEntryListener) cacheEntryListenerFactory.create();
}
private void injectDependencies(Object obj) {
ManagedContext managedContext = serializationService.getManagedContext();
managedContext.initialize(obj);
}
@Override
public CacheEntryListener getCacheEntryListener() {
return cacheEntryListener;
}
@Override
public void handleEvent(Object eventObject) {
if (eventObject instanceof CacheEventSet) {
CacheEventSet cacheEventSet = (CacheEventSet) eventObject;
try {
if (cacheEventSet.getEventType() != CacheEventType.COMPLETED) {
handleEvent(cacheEventSet.getEventType().getType(), cacheEventSet.getEvents());
}
} finally {
((CacheSyncListenerCompleter) source).countDownCompletionLatch(cacheEventSet.getCompletionId());
}
}
}
private void handleEvent(int type, Collection keys) {
final Iterable> cacheEntryEvent = createCacheEntryEvent(keys);
CacheEventType eventType = CacheEventType.getByType(type);
switch (eventType) {
case CREATED:
if (this.cacheEntryCreatedListener != null) {
this.cacheEntryCreatedListener.onCreated(cacheEntryEvent);
}
break;
case UPDATED:
if (this.cacheEntryUpdatedListener != null) {
this.cacheEntryUpdatedListener.onUpdated(cacheEntryEvent);
}
break;
case REMOVED:
if (this.cacheEntryRemovedListener != null) {
this.cacheEntryRemovedListener.onRemoved(cacheEntryEvent);
}
break;
case EXPIRED:
if (this.cacheEntryExpiredListener != null) {
this.cacheEntryExpiredListener.onExpired(cacheEntryEvent);
}
break;
default:
throw new IllegalArgumentException("Invalid event type: " + eventType.name());
}
}
private Iterable> createCacheEntryEvent(Collection keys) {
HashSet> evt = new HashSet>();
for (CacheEventData cacheEventData : keys) {
EventType eventType = CacheEventType.convertToEventType(cacheEventData.getCacheEventType());
K key = toObject(cacheEventData.getDataKey());
boolean hasNewValue = !(eventType == EventType.REMOVED || eventType == EventType.EXPIRED);
final V newValue;
final V oldValue;
if (isOldValueRequired) {
if (hasNewValue) {
newValue = toObject(cacheEventData.getDataValue());
oldValue = toObject(cacheEventData.getDataOldValue());
} else {
// according to contract of CacheEntryEvent#getValue
oldValue = toObject(cacheEventData.getDataValue());
newValue = oldValue;
}
} else {
if (hasNewValue) {
newValue = toObject(cacheEventData.getDataValue());
oldValue = null;
} else {
newValue = null;
oldValue = null;
}
}
final CacheEntryEventImpl event =
new CacheEntryEventImpl(source, eventType, key, newValue, oldValue);
if (filter == null || filter.evaluate(event)) {
evt.add(event);
}
}
return evt;
}
private T toObject(Data data) {
return serializationService.toObject(data);
}
public void handle(int type, Collection keys, int completionId) {
try {
if (CacheEventType.getByType(type) != CacheEventType.COMPLETED) {
handleEvent(type, keys);
}
} finally {
((CacheSyncListenerCompleter) source).countDownCompletionLatch(completionId);
}
}
@Override
public void onRegister(CacheService cacheService, String serviceName,
String topic, EventRegistration registration) {
CacheContext cacheContext = cacheService.getOrCreateCacheContext(topic);
cacheContext.increaseCacheEntryListenerCount();
}
@Override
public void onDeregister(CacheService cacheService, String serviceName,
String topic, EventRegistration registration) {
CacheContext cacheContext = cacheService.getOrCreateCacheContext(topic);
cacheContext.decreaseCacheEntryListenerCount();
}
@Override
public boolean eval(Object event) {
return true;
}
@Override
public Object getListener() {
return this;
}
@Override
public int getFactoryId() {
return CacheDataSerializerHook.F_ID;
}
@Override
public int getId() {
return CacheDataSerializerHook.CACHE_EVENT_LISTENER_ADAPTOR;
}
@Override
public void writeData(ObjectDataOutput out)
throws IOException {
}
@Override
public void readData(ObjectDataInput in)
throws IOException {
}
}