/** * Copyright [2011] [Datasalt Systems S.L.] * * 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.datasalt.utils.mapred.joiner; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import org.apache.hadoop.io.BytesWritable; import org.apache.hadoop.io.WritableComparable; import org.apache.hadoop.io.WritableComparator; import org.apache.hadoop.io.WritableUtils; import org.apache.hadoop.mapreduce.Partitioner; /** * This is the entity that will be serialized as Key when using the {@link MultiJoiner} Subclasses of this class may * establish a WritableComparable class that could be used for secondary sorting. By default, secondary sorting is only * applied at the level of the "channel" (see the MultiJoiner API). See {@link MultiJoinPairText} for an example of * subclass of this class. * * @author pere * * @param <K> */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class MultiJoinPair<K extends WritableComparable> implements WritableComparable<MultiJoinPair> { private BytesWritable group = new BytesWritable(); private int channelId = 0; private static final int INTEGER_BYTES = 4; private K secondarySort; private Class<K> secondarySortClass; public MultiJoinPair() throws InstantiationException, IllegalAccessException { this(null); } public MultiJoinPair(Class<K> secondaryKeyClass) throws InstantiationException, IllegalAccessException { this.secondarySortClass = secondaryKeyClass; if(this.secondarySortClass != null) { this.secondarySort = secondaryKeyClass.newInstance(); } } public int getChannelId() { return channelId; } public void setChannelId(int channel) { this.channelId = channel; } public BytesWritable getMultiJoinGroup() { return group; } public void setMultiJoinGroup(byte[] groupKey) { this.group.set(groupKey, 0, groupKey.length); } public void setMultiJoinGroup(byte[] groupKey, int offset, int length) { this.group.set(groupKey, offset, length); } public void setMultiJoinGroup(BytesWritable groupKey) { this.group.set(groupKey); } public K getSecondarySort() { return secondarySort; } public void setSecondSort(K second) { this.secondarySort = second; } public Class<K> getSecondarySortClass() { return secondarySortClass; } @Override public void write(DataOutput out) throws IOException { group.write(out); WritableUtils.writeVInt(out, channelId); if(secondarySortClass != null) { secondarySort.write(out); } } @Override public void readFields(DataInput in) throws IOException { group.readFields(in); channelId = WritableUtils.readVInt(in); if(secondarySortClass != null) { secondarySort.readFields(in); } } public boolean equals(Object o) { if(!(o instanceof MultiJoinPair<?>)) { return false; } MultiJoinPair<?> other = (MultiJoinPair<?>) o; return(group.equals(other.group) && (channelId == other.channelId) && (secondarySortClass == null || secondarySort .equals(other.secondarySort))); } public int compareTo(MultiJoinPair other) { int res = group.compareTo(other.group); if(res == 0) { if(channelId == other.channelId) { if(secondarySortClass != null) { return secondarySort.compareTo(other.secondarySort); } else { return 0; } } else { return channelId > other.channelId ? 1 : -1; } } else { return res; } } public int hashCode() { if(secondarySort != null) { return ((group.hashCode() * 31 + channelId) * 31 + secondarySort.hashCode()) & Integer.MAX_VALUE; } return (group.hashCode() * 31 + channelId) & Integer.MAX_VALUE; } /** * The binary comparator for the {@link MultiJoinPair} * * @author pere * */ public static class Comparator extends WritableComparator { Class<? extends WritableComparable> cl; public Comparator(Class<? extends WritableComparable> cl) { super(MultiJoinPair.class, true); this.cl = cl; } public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { try { int size1 = readInt(b1, s1); int size2 = readInt(b2, s2); int offset1, offset2; int cmp = WritableComparator.compareBytes(b1, s1 + INTEGER_BYTES, size1, b2, s2 + INTEGER_BYTES, size2); if(cmp == 0) { offset1 = s1 + INTEGER_BYTES + size1; offset2 = s2 + INTEGER_BYTES + size2; int secondarySort1 = readVInt(b1, offset1); int secondarySort2 = readVInt(b2, offset2); int variableSize1 = WritableUtils.decodeVIntSize(b1[offset1]); int variableSize2 = WritableUtils.decodeVIntSize(b2[offset2]); if(secondarySort1 == secondarySort2) { if(cl != null) { WritableComparator comparator = WritableComparator.get(cl); offset1 = offset1 + variableSize1; offset2 = offset2 + variableSize2; return comparator.compare(b1, offset1, l1 - offset1 + s1, b2, offset2, l2 - offset2 + s2); } else { return 0; } } else { return secondarySort1 > secondarySort2 ? 1 : -1; } } else { return cmp; } } catch(IOException e) { throw new RuntimeException(e); } } } /** * The Partitioner that partitions only by the first byte array * * @author pere * */ public static class GroupPartitioner extends Partitioner { @Override public int getPartition(Object key, Object value, int numPartitions) { MultiJoinPair pair = (MultiJoinPair) key; return ((pair.group.hashCode()) & Integer.MAX_VALUE) % numPartitions; } } /** * The Comparator that can be used as a Group Comparator by comparing only the first byte array * * @author pere,eric * */ public static class GroupComparator extends WritableComparator { public GroupComparator() { super(MultiJoinPair.class, true); } public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { int size1 = readInt(b1, s1); int size2 = readInt(b2, s2); return WritableComparator.compareBytes(b1, s1 + INTEGER_BYTES, size1, b2, s2 + INTEGER_BYTES, size2); } } static { // register this comparator WritableComparator.define(MultiJoinPair.class, new Comparator(null)); } }