View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.regionserver.wal;
22  
23  import java.io.IOException;
24  import java.lang.Class;
25  import java.lang.reflect.Constructor;
26  import java.lang.reflect.Field;
27   
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.fs.FSDataInputStream;
30  import org.apache.hadoop.fs.FileSystem;
31  import org.apache.hadoop.fs.Path;
32  import org.apache.hadoop.hbase.regionserver.wal.HLog;
33  import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
34  import org.apache.hadoop.io.SequenceFile;
35  import org.mortbay.log.Log;
36  
37  public class SequenceFileLogReader implements HLog.Reader {
38  
39    /**
40     * Hack just to set the correct file length up in SequenceFile.Reader.
41     * See HADOOP-6307.  The below is all about setting the right length on the
42     * file we are reading.  fs.getFileStatus(file).getLen() is passed down to
43     * a private SequenceFile.Reader constructor.  This won't work.  Need to do
44     * the available on the stream.  The below is ugly.  It makes getPos, the
45     * first time its called, return length of the file -- i.e. tell a lie -- just
46     * so this line up in SF.Reader's constructor ends up with right answer:
47     *
48     *         this.end = in.getPos() + length;
49     *
50     */
51    private static class WALReader extends SequenceFile.Reader {
52  
53      WALReader(final FileSystem fs, final Path p, final Configuration c)
54      throws IOException {
55        super(fs, p, c);
56  
57      }
58  
59      @Override
60      protected FSDataInputStream openFile(FileSystem fs, Path file,
61        int bufferSize, long length)
62      throws IOException {
63        return new WALReaderFSDataInputStream(super.openFile(fs, file,
64          bufferSize, length), length);
65      }
66  
67      /**
68       * Override just so can intercept first call to getPos.
69       */
70      static class WALReaderFSDataInputStream extends FSDataInputStream {
71        private boolean firstGetPosInvocation = true;
72        private long length;
73  
74        WALReaderFSDataInputStream(final FSDataInputStream is, final long l)
75        throws IOException {
76          super(is);
77          this.length = l;
78        }
79  
80        @Override
81        public long getPos() throws IOException {
82          if (this.firstGetPosInvocation) {
83            this.firstGetPosInvocation = false;
84            // Tell a lie.  We're doing this just so that this line up in
85            // SequenceFile.Reader constructor comes out with the correct length
86            // on the file:
87            //         this.end = in.getPos() + length;
88            long available = this.in.available();
89            // Length gets added up in the SF.Reader constructor so subtract the
90            // difference.  If available < this.length, then return this.length.
91            return available >= this.length? available - this.length: this.length;
92          }
93          return super.getPos();
94        }
95      }
96    }
97  
98    Configuration conf;
99    WALReader reader;
100   // Needed logging exceptions
101   Path path;
102   int edit = 0;
103   long entryStart = 0;
104 
105   public SequenceFileLogReader() { }
106 
107   @Override
108   public void init(FileSystem fs, Path path, Configuration conf)
109       throws IOException {
110     this.conf = conf;
111     this.path = path;
112     reader = new WALReader(fs, path, conf);
113   }
114 
115   @Override
116   public void close() throws IOException {
117     try {
118       reader.close();
119     } catch (IOException ioe) {
120       throw addFileInfoToException(ioe);
121     }
122   }
123 
124   @Override
125   public HLog.Entry next() throws IOException {
126     return next(null);
127   }
128 
129   @Override
130   public HLog.Entry next(HLog.Entry reuse) throws IOException {
131     this.entryStart = this.reader.getPosition();
132     HLog.Entry e = reuse;
133     if (e == null) {
134       HLogKey key = HLog.newKey(conf);
135       WALEdit val = new WALEdit();
136       e = new HLog.Entry(key, val);
137     }
138     boolean b = false;
139     try {
140       b = this.reader.next(e.getKey(), e.getEdit());
141     } catch (IOException ioe) {
142       throw addFileInfoToException(ioe);
143     }
144     edit++;
145     return b? e: null;
146   }
147 
148   @Override
149   public void seek(long pos) throws IOException {
150     try {
151       reader.seek(pos);
152     } catch (IOException ioe) {
153       throw addFileInfoToException(ioe);
154     }
155   }
156 
157   @Override
158   public long getPosition() throws IOException {
159     return reader.getPosition();
160   }
161 
162   private IOException addFileInfoToException(final IOException ioe)
163   throws IOException {
164     long pos = -1;
165     try {
166       pos = getPosition();
167     } catch (IOException e) {
168       Log.warn("Failed getting position to add to throw", e);
169     }
170 
171     // See what SequenceFile.Reader thinks is the end of the file
172     long end = Long.MAX_VALUE;
173     try {
174       Field fEnd = SequenceFile.Reader.class.getDeclaredField("end");
175       fEnd.setAccessible(true);
176       end = fEnd.getLong(this.reader);
177     } catch(Exception e) { /* reflection fail. keep going */ }
178 
179     String msg = (this.path == null? "": this.path.toString()) +
180       ", entryStart=" + entryStart + ", pos=" + pos + 
181       ((end == Long.MAX_VALUE) ? "" : ", end=" + end) + 
182       ", edit=" + this.edit;
183 
184     // Enhance via reflection so we don't change the original class type
185     try {
186       return (IOException) ioe.getClass()
187         .getConstructor(String.class)
188         .newInstance(msg)
189         .initCause(ioe);
190     } catch(Exception e) { /* reflection fail. keep going */ }
191     
192     return ioe;
193   }
194 }