/* * Copyright 2007 T-Rank AS * * 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 no.trank.openpipe.solr; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.URL; import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.stream.XMLStreamException; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.RequestEntity; import no.trank.openpipe.api.PipelineException; /** * @version $Revision$ */ public class SolrHttpDocumentPoster { private int docsPerPost = 0; private int unpostedDocs = 0; private int uncommitedDocs = 0; private String postUrl; private String encoding = "UTF-8"; private HttpClient httpClient = new HttpClient(); private SolrXmlDocumentWriter solrDocumentWriter; private boolean inAdd = false; private XMLBufferRequestEntity buf; private UpdateOptions updateOptions = new UpdateOptions(); private String user; private String password; private File storePostsDir; private int postCount = 0; private boolean prettyXML; public void prepare() throws IOException { URL url = new URL(postUrl); AuthScope authScope = new AuthScope(url.getHost(), url.getPort()); if (user != null) { httpClient.getState().setCredentials(authScope, new UsernamePasswordCredentials(user, password)); } if (storePostsDir != null && !storePostsDir.isDirectory()) { if (!storePostsDir.mkdirs()) { throw new IOException("No such directory: " + storePostsDir.getAbsolutePath()); } } } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public void setEncoding(String encoding) { this.encoding = encoding; } public void setPostUrl(String postUrl) { this.postUrl = postUrl; } public void setDocsPerPost(int docsPerPost) { this.docsPerPost = docsPerPost; } public UpdateOptions getUpdateOptions() { return updateOptions; } public void setUpdateOptions(UpdateOptions updateOptions) { this.updateOptions = updateOptions; } public File getStorePostsDir() { return storePostsDir; } public void setStorePostsDir(File storePostsDir) { this.storePostsDir = storePostsDir; } public boolean isPrettyXML() { return prettyXML; } public void setPrettyXML(boolean prettyXML) { this.prettyXML = prettyXML; } public void add(HashMap<String, List<String>> solrOutputDoc, Map<String, String> attribs) throws XMLStreamException, PipelineException { addDocument(solrOutputDoc, attribs); uncommitedDocs++; if (++unpostedDocs >= docsPerPost) { endAdd(); } } public void optimize() throws PipelineException, XMLStreamException { endAdd(); openPostStream(); solrDocumentWriter.optimize(); closePostStream(); } public void commit() throws XMLStreamException, PipelineException { endAdd(); openPostStream(); solrDocumentWriter.commit(); closePostStream(); uncommitedDocs = 0; } public void finish() throws XMLStreamException, PipelineException { if (uncommitedDocs > 0) { commit(); } } public void delete(List<String> fieldValueList) throws PipelineException, XMLStreamException { endAdd(); if (fieldValueList != null && !fieldValueList.isEmpty()) { openPostStream(); solrDocumentWriter.deleteById(fieldValueList); closePostStream(); } else { throw new PipelineException("Can not delete document. The id field is not set."); } uncommitedDocs++; } private void addDocument(HashMap<String, List<String>> solrOutputDoc, Map<String, String> attribs) throws XMLStreamException, PipelineException { startAdd(); solrDocumentWriter.startDoc(attribs); for (Map.Entry<String, List<String>> fieldEntry : solrOutputDoc.entrySet()) { for (String fieldValue : fieldEntry.getValue()) { solrDocumentWriter.writeField(fieldEntry.getKey(), fieldValue); } } solrDocumentWriter.endDoc(); } private void startAdd() throws PipelineException, XMLStreamException { if (!inAdd) { openPostStream(); solrDocumentWriter.startAdd(); inAdd = true; } } private void endAdd() throws XMLStreamException, PipelineException { if (inAdd) { solrDocumentWriter.endAdd(); inAdd = false; closePostStream(); unpostedDocs = 0; } } private void openPostStream() throws PipelineException { try { if (buf == null) { buf = new XMLBufferRequestEntity(4096); } else { buf.reset(); } solrDocumentWriter = new SolrXmlDocumentWriter(new OutputStreamWriter(buf, encoding), updateOptions, prettyXML); } catch (IOException e) { throw new PipelineException("Could not create post streams", e); } catch (XMLStreamException e) { throw new PipelineException("Could not create solrXmlDocWriter", e); } } private void closePostStream() throws PipelineException { // Closing the writer if (solrDocumentWriter != null) { try { solrDocumentWriter.close(); } catch (Exception e) { // Do nothing } solrDocumentWriter = null; } // Flushing the data to solr try { final PostMethod postMethod = new PostMethod(postUrl); try { postMethod.setRequestEntity(buf); writePostToFile(); int status = httpClient.executeMethod(postMethod); if (status < 200 || status >= 300) { throw new PipelineException("Solr post returned status: " + status + "\n" + postMethod.getResponseBodyAsString()); } } finally { postMethod.releaseConnection(); } } catch (IOException e) { throw new PipelineException("Could not post document(s) to solr", e); } finally { try { buf.close(); } catch (Exception e) { // Do nothing } buf = null; } } private void writePostToFile() throws IOException { if (storePostsDir != null) { final FileOutputStream fout = new FileOutputStream(new File(storePostsDir, createFileName())); try { buf.writeRequest(fout); } finally { try { fout.close(); } catch (IOException e) { // Ignoring } } } } private String createFileName() { return new Formatter().format("%1$04d.xml", postCount++).toString(); } private static class XMLBufferRequestEntity extends ByteArrayOutputStream implements RequestEntity { private XMLBufferRequestEntity(int size) { super(size); } @Override public boolean isRepeatable() { return true; } @Override public void writeRequest(OutputStream out) throws IOException { out.write(buf, 0, count); } @Override public long getContentLength() { return count; } @Override public String getContentType() { return "text/xml"; } } }