/*
* Copyright 2011 Internet Archive
*
* 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 org.archive.bacon.nutchwax;
import java.io.*;
import java.util.*;
import org.apache.hadoop.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.*;
import org.apache.pig.*;
import org.apache.pig.data.*;
import org.apache.pig.backend.hadoop.executionengine.mapReduceLayer.PigSplit;
import org.apache.pig.backend.executionengine.ExecException;
import org.apache.nutch.parse.*;
import org.apache.nutch.metadata.Metadata;
/**
* Apache Pig UDF to load parsed_content records from a Nutch(WAX)
* segment.
*
* This loader assumes that the path is to the 'parse_text'
* sub-directory of a Nutch(WAX) segment.
*
* It returns a Tuple for each link, of the form:
* (url:chararray,
* digest:chararray,
* content:chararray)
*/
public class ContentLoader extends LoadFunc
{
private RecordReader<Text,Writable> reader;
/**
* The Nutch(WAX) "parse_data" segment is just a SequenceFile
* with Text keys and Writable values.
*/
public InputFormat getInputFormat( )
throws IOException
{
return new SequenceFileInputFormat<Text,Writable>( );
}
/**
* Reads a Nutch(WAX) metadata record and returns a Tuple containing
* the metadata values. Any null String values are returned as "",
* but since 'length' is a Long, we return null if there is not a
* length value for a record.
*/
public Tuple getNext( )
throws IOException
{
try
{
if ( ! reader.nextKeyValue( ) )
{
return null;
}
String key = this.reader.getCurrentKey().toString();
int lastSpace = key.lastIndexOf( ' ' );
if ( lastSpace < 0 || (lastSpace == key.length() - 1) ) return null;
String url = key.substring( 0, lastSpace );
String digest = key.substring( lastSpace + 1 );
Writable value;
while ( ( value = this.reader.getCurrentValue( ) ) != null )
{
// Whoah, what to do? Skip it for now
if ( ! ( value instanceof ParseText ) ) continue ;
ParseText pt = (ParseText) value;
String content = pt.toString();
Tuple tuple = TupleFactory.getInstance( ).newTuple( );
tuple.append( url );
tuple.append( digest );
tuple.append( content );
return tuple;
}
return null;
}
catch ( InterruptedException e )
{
// From the Pig example/howto code.
int errCode = 6018;
String errMsg = "Error while reading input";
throw new ExecException(errMsg, errCode,PigException.REMOTE_ENVIRONMENT, e);
}
}
/**
* Just save the given reader. Dunno what to do with the 'split'.
*/
public void prepareToRead( RecordReader reader, PigSplit split )
throws IOException
{
this.reader = reader;
}
/**
* The 'location' is a path string, which could contain wildcards.
* Expand the wildcards and add each matching path to the input.
*/
public void setLocation( String location, Job job )
throws IOException
{
// Expand any filename globs, and add each to the input paths.
FileStatus[] files = FileSystem.get( job.getConfiguration( ) ).globStatus( new Path( location ) );
for ( FileStatus file : files )
{
FileInputFormat.addInputPath( job, new Path( file.getPath( ), "parse_text" ) );
}
}
}