/** * Copyright 2011-2017 Asakusa Framework Team. * * 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.asakusafw.runtime.io.util; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.text.MessageFormat; import org.apache.hadoop.io.WritableComparable; import org.apache.hadoop.io.WritableComparator; import org.apache.hadoop.util.ReflectionUtils; /** * An abstract implementation of shuffling keys. * @param <TGroup> type for grouping * @param <TOrder> type for ordering * @since 0.2.5 */ public abstract class ShuffleKey< TGroup extends WritableRawComparable, TOrder extends WritableRawComparable> implements WritableRawComparable { final TGroup groupObject; final TOrder orderObject; /** * Creates a new instance. * @param groupType type for grouping * @param orderType tyoe for ordering * @throws IllegalArgumentException if some parameters were {@code null} */ protected ShuffleKey(Class<TGroup> groupType, Class<TOrder> orderType) { if (groupType == null) { throw new IllegalArgumentException("groupType must not be null"); //$NON-NLS-1$ } if (orderType == null) { throw new IllegalArgumentException("orderType must not be null"); //$NON-NLS-1$ } this.groupObject = ReflectionUtils.newInstance(groupType, null); this.orderObject = ReflectionUtils.newInstance(orderType, null); } /** * Creates a new instance. * @param groupObject object for grouping * @param orderObject object for ordering * @throws IllegalArgumentException if some parameters were {@code null} */ protected ShuffleKey(TGroup groupObject, TOrder orderObject) { if (groupObject == null) { throw new IllegalArgumentException("groupObject must not be null"); //$NON-NLS-1$ } if (orderObject == null) { throw new IllegalArgumentException("orderObject must not be null"); //$NON-NLS-1$ } this.groupObject = groupObject; this.orderObject = orderObject; } /** * Returns an object for grouping. * @return the group object */ public final TGroup getGroupObject() { return groupObject; } /** * Returns an object for ordering. * @return the order object */ public final TOrder getOrderObject() { return orderObject; } @Override public final int compareTo(WritableRawComparable o) { ShuffleKey<?, ?> other = (ShuffleKey<?, ?>) o; int groupDiff = groupObject.compareTo(other.groupObject); if (groupDiff != 0) { return groupDiff; } int orderDiff = orderObject.compareTo(other.orderObject); return orderDiff; } @Override public final void write(DataOutput out) throws IOException { groupObject.write(out); orderObject.write(out); } @Override public final void readFields(DataInput in) throws IOException { groupObject.readFields(in); orderObject.readFields(in); } @Override public final int getSizeInBytes(byte[] buf, int offset) throws IOException { int groupSize = groupObject.getSizeInBytes(buf, offset); int orderSize = orderObject.getSizeInBytes(buf, offset + groupSize); return groupSize + orderSize; } @Override public final int compareInBytes(byte[] b1, int o1, byte[] b2, int o2) throws IOException { int groupDiff = groupObject.compareInBytes(b1, o1, b2, o2); if (groupDiff != 0) { return groupDiff; } int groupSize = groupObject.getSizeInBytes(b1, o1); assert groupSize == groupObject.getSizeInBytes(b2, o2); int orderDiff = orderObject.compareInBytes(b1, o1 + groupSize, b2, o2 + groupSize); return orderDiff; } @Override public final int hashCode() { final int prime = 31; int result = 1; result = prime * result + groupObject.hashCode(); result = prime * result + orderObject.hashCode(); return result; } @Override public final boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ShuffleKey<?, ?> other = (ShuffleKey<?, ?>) obj; if (!groupObject.equals(other.groupObject)) { return false; } if (!orderObject.equals(other.orderObject)) { return false; } return true; } @Override public final String toString() { return MessageFormat.format( "ShuffleKey(group={0}, order={1})", //$NON-NLS-1$ groupObject, orderObject); } /** * An implementation of partitioner for {@link ShuffleKey}. * @since 0.2.5 */ @SuppressWarnings("rawtypes") public static final class Partitioner extends org.apache.hadoop.mapreduce.Partitioner<ShuffleKey, Object> { @Override public int getPartition(ShuffleKey key, Object value, int numPartitions) { int hash = key.groupObject.hashCode() & Integer.MAX_VALUE; return hash % numPartitions; } } /** * An abstract implementation of grouping comparator for {@link ShuffleKey}. * @since 0.2.5 */ @SuppressWarnings("rawtypes") public abstract static class AbstractGroupComparator extends WritableComparator { private final ShuffleKey<?, ?> object; /** * Creates a new instance. * @param keyClass the key class */ protected AbstractGroupComparator(Class<? extends ShuffleKey> keyClass) { super(keyClass); this.object = (ShuffleKey<?, ?>) newKey(); } @Override public int compare(WritableComparable a, WritableComparable b) { ShuffleKey<?, ?> ak = (ShuffleKey<?, ?>) a; ShuffleKey<?, ?> bk = (ShuffleKey<?, ?>) b; return ak.groupObject.compareTo(bk.groupObject); } @Override public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { try { return object.groupObject.compareInBytes(b1, s1, b2, s2); } catch (IOException e) { throw new IllegalStateException(e); } } } /** * An abstract implementation of ordering comparator for {@link ShuffleKey}. * @since 0.2.5 */ @SuppressWarnings("rawtypes") public abstract static class AbstractOrderComparator extends WritableRawComparator { /** * Creates a new instance. * @param keyClass the key class */ protected AbstractOrderComparator(Class<? extends ShuffleKey> keyClass) { super(keyClass); } } }