Java tutorial
/* It is a application for event distribution to event n-consumers with m-sources. Copyright (C) 2010 "Imran M Yousuf <imran@smartitengineering.com>" This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.smartitengineering.event.hub.spi.hbase; import com.google.inject.Inject; import com.smartitengineering.dao.common.CommonReadDao; import com.smartitengineering.dao.common.CommonWriteDao; import com.smartitengineering.dao.common.queryparam.MatchMode; import com.smartitengineering.dao.common.queryparam.QueryParameter; import com.smartitengineering.dao.common.queryparam.QueryParameterFactory; import com.smartitengineering.dao.impl.hbase.spi.RowCellIncrementor; import com.smartitengineering.event.hub.api.Channel; import com.smartitengineering.event.hub.api.Event; import com.smartitengineering.event.hub.spi.HubPersistentStorer; import com.smartitengineering.event.hub.spi.hbase.persistents.RowAutoIdIndex; import com.smartitengineering.event.hub.spi.hbase.persistents.ChannelAdapterHelper; import com.smartitengineering.event.hub.spi.hbase.persistents.EventAdapterHelper; import com.smartitengineering.event.hub.spi.hbase.persistents.EventId; import com.smartitengineering.event.hub.spi.hbase.persistents.EventUUID; import com.smartitengineering.event.hub.spi.hbase.persistents.PersistentChannel; import com.smartitengineering.event.hub.spi.hbase.persistents.PersistentEvent; import com.smartitengineering.event.hub.spi.hbase.persistents.ReverseIdIndex; import com.smartitengineering.util.bean.adapter.GenericAdapter; import com.smartitengineering.util.bean.adapter.GenericAdapterImpl; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.hadoop.hbase.util.Bytes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author imyousuf */ public class HubPersistentStorerImpl implements HubPersistentStorer { private static final String CHANNELS_ROW_ID_N_PREFIX = "channels"; private static final String EVENTS_ROW_ID_N_PREFIX = "events"; private static final PersistentChannel[] EMPTY_CHANNEL_ARRAY = new PersistentChannel[0]; private static final PersistentEvent[] EMPTY_EVENT_ARRAY = new PersistentEvent[0]; public static final int MAX_LENGTH = String.valueOf(Long.MAX_VALUE).length(); @Inject protected CommonWriteDao<PersistentChannel> channelWrtDao; @Inject protected CommonWriteDao<PersistentEvent> eventWrtDao; @Inject protected CommonWriteDao<EventUUID> eventUUIDWrtDao; @Inject protected CommonWriteDao<RowAutoIdIndex> autoIdWrtDao; @Inject protected CommonWriteDao<ReverseIdIndex> reverseIdIndexWrtDao; @Inject protected CommonReadDao<PersistentChannel, Long> channelRdDao; @Inject protected CommonReadDao<PersistentEvent, EventId> eventRdDao; @Inject protected CommonReadDao<EventUUID, String> eventUUIDRdDao; @Inject protected CommonReadDao<RowAutoIdIndex, String> autoIdRdDao; @Inject protected CommonReadDao<ReverseIdIndex, String> reverseIdIndexRdDao; @Inject protected RowCellIncrementor<Channel, RowAutoIdIndex, String> idIncrementor; @Inject protected RowCellIncrementor<ReverseIdIndex, RowAutoIdIndex, String> reverseIdIncrementor; protected boolean channelAutoIdInitialized = false; protected boolean eventAutoIdInitialized = false; protected final GenericAdapter<Channel, PersistentChannel> channelAdapter; protected final GenericAdapter<Event, PersistentEvent> eventAdapter; protected Logger logger = LoggerFactory.getLogger(getClass()); public HubPersistentStorerImpl() { GenericAdapterImpl<Channel, PersistentChannel> lChannelAdapter = new GenericAdapterImpl<Channel, PersistentChannel>(); lChannelAdapter.setHelper(new ChannelAdapterHelper()); this.channelAdapter = lChannelAdapter; GenericAdapterImpl<Event, PersistentEvent> lEventAdapter = new GenericAdapterImpl<Event, PersistentEvent>(); lEventAdapter.setHelper(new EventAdapterHelper()); this.eventAdapter = lEventAdapter; } @Override public void create(Channel channel) { checkAndInitializeAutoId(); if (!channelAutoIdInitialized) { throw new IllegalStateException("Channel ID could not be generated!"); } if (channel == null) { return; } PersistentChannel pChannel = channelAdapter.convert(channel); if (!pChannel.isValid()) { throw new IllegalStateException("Channel not in valid state!"); } Channel eChannel = getChannel(pChannel.getName()); if (eChannel != null) { throw new IllegalStateException("Channel already exists!"); } try { long channelId = idIncrementor.incrementAndGet(CHANNELS_ROW_ID_N_PREFIX, -1); long channelReverseId = reverseIdIncrementor.incrementAndGet(CHANNELS_ROW_ID_N_PREFIX, 1); pChannel.setId(channelId); final Date date = new Date(); pChannel.setCreationDateTime(date); pChannel.setLastModifiedDateTime(date); RowAutoIdIndex channelEventId = new RowAutoIdIndex(); channelEventId.setAutoIdValue(Long.MAX_VALUE); channelEventId.setId(getChannelIdIndexName(pChannel.getName())); channelEventId.setReverseId(String.valueOf(channelId)); ReverseIdIndex idIndex = new ReverseIdIndex(); idIndex.setId(getChannelIdIndexName(String.valueOf(channelReverseId))); idIndex.setReverseId(String.valueOf(channelId)); autoIdWrtDao.save(channelEventId); channelWrtDao.save(pChannel); reverseIdIndexWrtDao.save(idIndex); } catch (RuntimeException ex) { logger.error("Could not create channel or its events' auto id generator!", ex); throw ex; } } @Override public void update(Channel channel) { if (channel == null) { return; } checkAndInitializeAutoId(); PersistentChannel pChannel = getMergedPersistentChannel(channel); if (!pChannel.isValid()) { throw new IllegalStateException("Channel not in valid state!"); } try { pChannel.setLastModifiedDateTime(new Date()); channelWrtDao.update(pChannel); } catch (RuntimeException ex) { logger.error("Could not update channel!", ex); throw ex; } } @Override public void delete(Channel channel) { if (channel == null) { return; } checkAndInitializeAutoId(); PersistentChannel pChannel = getMergedPersistentChannel(channel); if (!pChannel.isValid()) { throw new IllegalStateException("Channel not in valid state!"); } try { channelWrtDao.delete(pChannel); } catch (RuntimeException ex) { logger.error("Could not update channel!", ex); throw ex; } } @Override public Channel getChannel(String channelName) { if (StringUtils.isBlank(channelName)) { return null; } return channelAdapter.convertInversely(getPersistentChannel(channelName)); } @Override public Collection<Channel> getChannels(int startIndex, int count) { if (count == 0) { return Collections.emptyList(); } final QueryParameter<Integer> maxResultsParam = QueryParameterFactory.getMaxResultsParam(Math.abs(count)); if (count < 0) { final QueryParameter param; long index = Long.MAX_VALUE - startIndex; if (logger.isDebugEnabled()) { logger.debug("Reverse Index value " + index); } param = QueryParameterFactory.getGreaterThanPropertyParam("id", Bytes.toBytes(index)); final List<PersistentChannel> list = channelRdDao.getList(param, maxResultsParam); if (logger.isDebugEnabled()) { logger.debug("Result " + list); } return channelAdapter.convertInversely(list.toArray(EMPTY_CHANNEL_ARRAY)); } else { final QueryParameter param = QueryParameterFactory.getGreaterThanPropertyParam("id", Bytes.toBytes(getChannelIdIndexName(String.valueOf(startIndex)))); List<ReverseIdIndex> reverseIndexes = reverseIdIndexRdDao.getList(param, maxResultsParam); List<Long> ids = new ArrayList<Long>(reverseIndexes.size()); for (ReverseIdIndex index : reverseIndexes) { final long longVal = NumberUtils.toLong(index.getReverseId()); if (longVal > -1) { ids.add(longVal); } } final Set<PersistentChannel> byIds = channelRdDao.getByIds(ids); if (logger.isDebugEnabled()) { logger.debug("Result " + reverseIndexes + " - " + byIds); } return channelAdapter.convertInversely(byIds.toArray(EMPTY_CHANNEL_ARRAY)); } } @Override public Event create(Channel channel, Event event) { PersistentEvent persistentEvent = eventAdapter.convert(event); if (persistentEvent != null && channel != null) { String eventsId = EVENTS_ROW_ID_N_PREFIX; long placheholderId = idIncrementor.incrementAndGet(eventsId, -1); long revPlacheholderId = reverseIdIncrementor.incrementAndGet(eventsId, 1); persistentEvent.setPlaceholderId(String.valueOf(placheholderId)); persistentEvent.setChannelId(channel.getName()); persistentEvent.getId().setEventIdForChannel(placheholderId); persistentEvent.setCreationDateTime(new Date()); if (StringUtils.isBlank(persistentEvent.getUuid())) { UUID uuid = UUID.randomUUID(); persistentEvent.setUuid(uuid.toString()); } else { Event possDupEvent = getEventByUUID(persistentEvent.getUuid()); if (possDupEvent != null) { throw new IllegalArgumentException("Duplication event!"); } } EventUUID eUuid = new EventUUID(); eUuid.setEventId(persistentEvent.getId()); eUuid.setId(persistentEvent.getUuid()); ReverseIdIndex reverseIdIndex = new ReverseIdIndex(); reverseIdIndex.setReverseId(persistentEvent.getId().toString()); reverseIdIndex.setId(new StringBuilder().append(leftPadNumberWithZero(revPlacheholderId)).append(':') .append(channel.getName()).toString()); eventWrtDao.save(persistentEvent); reverseIdIndexWrtDao.save(reverseIdIndex); eventUUIDWrtDao.save(eUuid); final Event convertInversely = eventAdapter.convertInversely(persistentEvent); if (logger.isDebugEnabled()) { logger.debug("Event's ID, UUID and PlaceholderID: " + persistentEvent.getId().toString() + " " + convertInversely.getUniversallyUniqueID() + " " + convertInversely.getPlaceholderId()); } return convertInversely; } return null; } @Override public void delete(Event event) { if (event == null) { return; } final PersistentEvent persistentEvent = getPersistentEvent(NumberUtils.toLong(event.getPlaceholderId())); if (persistentEvent == null) { return; } eventWrtDao.delete(persistentEvent); } @Override public Event getEvent(String placeholderId) { final PersistentEvent persistentEvent = getPersistentEvent(NumberUtils.toLong(placeholderId)); final Event convertInversely = eventAdapter.convertInversely(persistentEvent); if (logger.isDebugEnabled()) { if (persistentEvent != null && convertInversely != null) { logger.debug("Event's ID, UUID and PlaceholderID: " + persistentEvent.getId().toString() + " " + convertInversely.getUniversallyUniqueID() + " " + convertInversely.getPlaceholderId()); } else { logger.info("EVENT IS NULL!"); } } return convertInversely; } @Override public Event getEventByUUID(String uuid) { if (StringUtils.isBlank(uuid)) { return null; } EventUUID eUuid = eventUUIDRdDao.getById(uuid); if (eUuid != null) { PersistentEvent event = eventRdDao.getById(eUuid.getEventId()); if (event != null) { return eventAdapter.convertInversely(event); } } return null; } @Override public LinkedHashSet<Event> getEvents(String placeholderId, final String channelId, int count) { if (count == 0) { return new LinkedHashSet<Event>(); } final QueryParameter<Integer> maxResultsParam = QueryParameterFactory.getMaxResultsParam(Math.abs(count)); final String eventChannelId; long eventId = NumberUtils.toLong(placeholderId); if (eventId > -1) { if (StringUtils.isNotBlank(placeholderId)) { PersistentEvent pEvent = getPersistentEvent(eventId); if (pEvent != null) { eventChannelId = pEvent.getChannelId(); } else { eventChannelId = ""; } } else { eventChannelId = channelId; } } else { placeholderId = null; eventChannelId = ""; if (count > 0) { count = -1 * count; } } if (count < 0) { final List<QueryParameter> params = new ArrayList<QueryParameter>(); params.add(maxResultsParam); if (StringUtils.isNotBlank(placeholderId)) { final StringBuilder searchId = new StringBuilder( leftPadNumberWithZero(NumberUtils.toLong(placeholderId))).append(':'); searchId.append(eventChannelId); if (logger.isDebugEnabled()) { logger.debug("Event Id to search greater or smaller than: " + searchId); } params.add(QueryParameterFactory.getGreaterThanPropertyParam("id", Bytes.toBytes(searchId.toString()))); } if (StringUtils.isNotBlank(channelId)) { String toString; toString = new StringBuilder("[0-9]+").append(':').append(channelId.toLowerCase()).toString(); if (logger.isDebugEnabled()) { logger.debug("Channel to search with id at end " + toString); } params.add(QueryParameterFactory.getStringLikePropertyParam("id", toString, MatchMode.END)); } logger.debug("Doing event straight search!"); final List<PersistentEvent> list = eventRdDao.getList(params); if (logger.isDebugEnabled()) { logger.debug("Straight event search completed!"); for (PersistentEvent pEvent : list) { logger.debug("EVENT " + pEvent.getId().toString()); } } return new LinkedHashSet<Event>(eventAdapter.convertInversely(list.toArray(EMPTY_EVENT_ARRAY))); } else { final List<QueryParameter> params = new ArrayList<QueryParameter>(); params.add(maxResultsParam); if (StringUtils.isNotBlank(placeholderId)) { final long toLong = NumberUtils.toLong(placeholderId); final long reversePlaceholderId = Long.MAX_VALUE - toLong; params.add(QueryParameterFactory.getGreaterThanPropertyParam("id", Bytes.toBytes(new StringBuilder(leftPadNumberWithZero(reversePlaceholderId)).append(':') .append(eventChannelId).toString()))); } final String toString; if (StringUtils.isNotBlank(channelId)) { toString = new StringBuilder("[0-9]+").append("\\:").append(channelId.toLowerCase()).toString(); } else { toString = new StringBuilder("[0-9]+").append("\\:").append(".+").toString(); } if (logger.isDebugEnabled()) { logger.debug("End pattern to test for! " + toString); } params.add(QueryParameterFactory.getStringLikePropertyParam("id", toString, MatchMode.END)); logger.debug("Doing reverse event search!"); List<ReverseIdIndex> indexes = reverseIdIndexRdDao.getList(params); List<EventId> eventIds = new ArrayList<EventId>(indexes.size()); for (ReverseIdIndex index : indexes) { if (logger.isDebugEnabled()) { logger.debug("Reverse ID: " + index.getReverseId()); } eventIds.add(EventId.fromString(index.getReverseId())); } Collections.reverse(eventIds); final Set<PersistentEvent> byIds = eventRdDao.getByIds(eventIds); logger.debug("Reverse event search completed!"); return new LinkedHashSet<Event>(eventAdapter.convertInversely(byIds.toArray(EMPTY_EVENT_ARRAY))); } } protected String getChannelIdIndexName(String channelName) { return new StringBuilder(CHANNELS_ROW_ID_N_PREFIX).append(':').append(channelName).toString(); } protected String getEventIdIndexName(String eventId) { return new StringBuilder(EVENTS_ROW_ID_N_PREFIX).append(':').append(eventId).toString(); } protected void checkAndInitializeAutoId() throws RuntimeException { if (!channelAutoIdInitialized) { channelAutoIdInitialized = checkAndInitializeAutoId(CHANNELS_ROW_ID_N_PREFIX); } if (!eventAutoIdInitialized) { eventAutoIdInitialized = checkAndInitializeAutoId(EVENTS_ROW_ID_N_PREFIX); } } protected boolean checkAndInitializeAutoId(String autoId) throws RuntimeException { RowAutoIdIndex id = autoIdRdDao.getById(autoId); if (id == null) { id = new RowAutoIdIndex(); id.setAutoIdValue(Long.MAX_VALUE); id.setReverseAutoIdValue(0l); id.setId(autoId); try { autoIdWrtDao.save(id); return true; } catch (RuntimeException ex) { logger.error("Could not initialize channel auto id!", ex); throw ex; } } else { return true; } } protected String leftPadNumberWithZero(long revPlacheholderId) { return StringUtils.leftPad(String.valueOf(revPlacheholderId), MAX_LENGTH, '0'); } protected PersistentChannel getMergedPersistentChannel(Channel channel) { if (channel == null || StringUtils.isBlank(channel.getName())) { return null; } PersistentChannel persistentChannel = getPersistentChannel(channel.getName()); if (logger.isDebugEnabled()) { logger.debug("Persistent channel found is " + persistentChannel); logger.debug("Persistent channel ID is " + persistentChannel.getId()); } Map.Entry<Channel, PersistentChannel> entry; entry = new SimpleEntry<Channel, PersistentChannel>(channel, persistentChannel); channelAdapter.merge(entry); if (logger.isDebugEnabled()) { logger.debug("Persistent channel ID is " + persistentChannel.getId()); } return persistentChannel; } protected PersistentChannel getPersistentChannel(String channelName) { if (StringUtils.isBlank(channelName)) { return null; } final String channelIdIndexName = getChannelIdIndexName(channelName.toLowerCase()); if (logger.isDebugEnabled()) { logger.debug("Getting channel with name in reverse lookup table " + channelIdIndexName); } RowAutoIdIndex idIndex = autoIdRdDao.getById(channelIdIndexName); if (idIndex != null) { logger.debug("Found ID in channel name index"); final long channelId = NumberUtils.toLong(idIndex.getReverseId()); if (logger.isDebugEnabled()) { logger.debug("Found reverse index for channel " + channelId); } PersistentChannel persistentChannel = channelRdDao.getById(channelId); return persistentChannel; } else { logger.debug("Did not found ID in channel name index"); PersistentChannel persistentChannel = channelRdDao .getSingle(QueryParameterFactory.getStringLikePropertyParam(PersistentChannel.NAME, channelName.toLowerCase(), MatchMode.EXACT)); return persistentChannel; } } protected PersistentEvent getMergedPersistentEvent(Event event) { if (event == null) { return null; } PersistentEvent persistentEvent = getPersistentEvent(NumberUtils.toLong(event.getPlaceholderId())); Map.Entry<Event, PersistentEvent> entry; entry = new SimpleEntry<Event, PersistentEvent>(event, persistentEvent); eventAdapter.merge(entry); return persistentEvent; } protected PersistentEvent getPersistentEvent(long placeholderId) { if (placeholderId <= 0) { logger.debug("Invalid place holder id!"); return null; } PersistentEvent persistentEvent = eventRdDao.getSingle(QueryParameterFactory .getStringLikePropertyParam("id", leftPadNumberWithZero(placeholderId), MatchMode.START)); return persistentEvent; } }