/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.tajo.storage.fragment;
import com.google.common.base.Objects;
import com.google.gson.annotations.Expose;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.Path;
import org.apache.tajo.util.TUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.apache.tajo.catalog.proto.CatalogProtos.FileFragmentProto;
import static org.apache.tajo.catalog.proto.CatalogProtos.FragmentProto;
public class FileFragment implements Fragment, Comparable<FileFragment>, Cloneable {
@Expose private String tableName; // required
@Expose private Path uri; // required
@Expose private Long startOffset; // required
@Expose private Long length; // required
private String[] hosts; // Datanode hostnames
@Expose private int[] diskIds;
public FileFragment(ByteString raw) throws InvalidProtocolBufferException {
FileFragmentProto.Builder builder = FileFragmentProto.newBuilder();
builder.mergeFrom(raw);
builder.build();
init(builder.build());
}
public FileFragment(String tableName, Path uri, BlockLocation blockLocation, int[] diskIds)
throws IOException {
this.set(tableName, uri, blockLocation.getOffset(), blockLocation.getLength(),
blockLocation.getHosts(), diskIds);
}
// Non splittable
public FileFragment(String tableName, Path uri, long start, long length, String[] hosts) {
this.set(tableName, uri, start, length, null, null);
this.hosts = hosts;
}
public FileFragment(String fragmentId, Path path, long start, long length) {
this.set(fragmentId, path, start, length, null, null);
}
public FileFragment(FileFragmentProto proto) {
init(proto);
}
private void init(FileFragmentProto proto) {
int[] diskIds = new int[proto.getDiskIdsList().size()];
int i = 0;
for(Integer eachValue: proto.getDiskIdsList()) {
diskIds[i++] = eachValue;
}
this.set(proto.getId(), new Path(proto.getPath()),
proto.getStartOffset(), proto.getLength(),
proto.getHostsList().toArray(new String[]{}),
diskIds);
}
private void set(String tableName, Path path, long start,
long length, String[] hosts, int[] diskIds) {
this.tableName = tableName;
this.uri = path;
this.startOffset = start;
this.length = length;
this.hosts = hosts;
this.diskIds = diskIds;
}
/**
* Get the list of hosts (hostname) hosting this block
*/
public String[] getHosts() {
if (hosts == null) {
this.hosts = new String[0];
}
return hosts;
}
/**
* Get the list of Disk Ids
* Unknown disk is -1. Others 0 ~ N
*/
public int[] getDiskIds() {
if (diskIds == null) {
this.diskIds = new int[getHosts().length];
Arrays.fill(this.diskIds, -1);
}
return diskIds;
}
public String getTableName() {
return this.tableName;
}
public Path getPath() {
return this.uri;
}
public void setPath(Path path) {
this.uri = path;
}
public Long getStartKey() {
return this.startOffset;
}
public Long getEndKey() {
return this.length;
}
/**
*
* The offset range of tablets <b>MUST NOT</b> be overlapped.
*
* @param t
* @return If the table paths are not same, return -1.
*/
@Override
public int compareTo(FileFragment t) {
if (getPath().equals(t.getPath())) {
long diff = this.getStartKey() - t.getStartKey();
if (diff < 0) {
return -1;
} else if (diff > 0) {
return 1;
} else {
return 0;
}
} else {
return -1;
}
}
@Override
public boolean equals(Object o) {
if (o instanceof FileFragment) {
FileFragment t = (FileFragment) o;
if (getPath().equals(t.getPath())
&& TUtil.checkEquals(t.getStartKey(), this.getStartKey())
&& TUtil.checkEquals(t.getEndKey(), this.getEndKey())) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(tableName, uri, startOffset, length);
}
public Object clone() throws CloneNotSupportedException {
FileFragment frag = (FileFragment) super.clone();
frag.tableName = tableName;
frag.uri = uri;
frag.diskIds = diskIds;
frag.hosts = hosts;
return frag;
}
@Override
public String toString() {
return "\"fragment\": {\"id\": \""+ tableName +"\", \"path\": "
+getPath() + "\", \"start\": " + this.getStartKey() + ",\"length\": "
+ getEndKey() + "}" ;
}
public FragmentProto getProto() {
FileFragmentProto.Builder builder = FileFragmentProto.newBuilder();
builder.setId(this.tableName);
builder.setStartOffset(this.startOffset);
builder.setLength(this.length);
builder.setPath(this.uri.toString());
if(diskIds != null) {
List<Integer> idList = new ArrayList<Integer>();
for(int eachId: diskIds) {
idList.add(eachId);
}
builder.addAllDiskIds(idList);
}
if(hosts != null) {
builder.addAllHosts(TUtil.newList(hosts));
}
FragmentProto.Builder fragmentBuilder = FragmentProto.newBuilder();
fragmentBuilder.setId(this.tableName);
fragmentBuilder.setContents(builder.buildPartial().toByteString());
return fragmentBuilder.build();
}
}