001: package org.apache.solr.util;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import java.io.File;
021: import java.io.FileInputStream;
022: import java.io.FileNotFoundException;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.InputStreamReader;
026: import java.io.OutputStream;
027: import java.io.OutputStreamWriter;
028: import java.io.Reader;
029: import java.io.StringReader;
030: import java.io.StringWriter;
031: import java.io.UnsupportedEncodingException;
032: import java.io.Writer;
033: import java.util.Set;
034: import java.util.HashSet;
035: import java.net.HttpURLConnection;
036: import java.net.MalformedURLException;
037: import java.net.ProtocolException;
038: import java.net.URL;
039:
040: /**
041: * A simple utility class for posting raw updates to a Solr server,
042: * has a main method so it can be run on the command line.
043: *
044: */
045: public class SimplePostTool {
046: public static final String DEFAULT_POST_URL = "http://localhost:8983/solr/update";
047: public static final String POST_ENCODING = "UTF-8";
048: public static final String VERSION_OF_THIS_TOOL = "1.2";
049: private static final String SOLR_OK_RESPONSE_EXCERPT = "<int name=\"status\">0</int>";
050:
051: private static final String DEFAULT_COMMIT = "yes";
052:
053: private static final String DATA_MODE_FILES = "files";
054: private static final String DATA_MODE_ARGS = "args";
055: private static final String DATA_MODE_STDIN = "stdin";
056: private static final String DEFAULT_DATA_MODE = DATA_MODE_FILES;
057:
058: private static final Set<String> DATA_MODES = new HashSet<String>();
059: static {
060: DATA_MODES.add(DATA_MODE_FILES);
061: DATA_MODES.add(DATA_MODE_ARGS);
062: DATA_MODES.add(DATA_MODE_STDIN);
063: }
064:
065: protected URL solrUrl;
066:
067: private class PostException extends RuntimeException {
068: PostException(String reason, Throwable cause) {
069: super (reason + " (POST URL=" + solrUrl + ")", cause);
070: }
071: }
072:
073: public static void main(String[] args) {
074: info("version " + VERSION_OF_THIS_TOOL);
075:
076: if (0 < args.length && "-help".equals(args[0])) {
077: System.out
078: .println("This is a simple command line tool for POSTing raw XML to a Solr\n"
079: + "port. XML data can be read from files specified as commandline\n"
080: + "args; as raw commandline arg strings; or via STDIN.\n"
081: + "Examples:\n"
082: + " java -Ddata=files -jar post.jar *.xml\n"
083: + " java -Ddata=args -jar post.jar '<delete><id>42</id></delete>'\n"
084: + " java -Ddata=stdin -jar post.jar < hd.xml\n"
085: + "Other options controlled by System Properties include the Solr\n"
086: + "URL to POST to, and whether a commit should be executed. These\n"
087: + "are the defaults for all System Properties...\n"
088: + " -Ddata="
089: + DEFAULT_DATA_MODE
090: + "\n"
091: + " -Durl="
092: + DEFAULT_POST_URL
093: + "\n"
094: + " -Dcommit=" + DEFAULT_COMMIT + "\n");
095: return;
096: }
097:
098: URL u = null;
099: try {
100: u = new URL(System.getProperty("url", DEFAULT_POST_URL));
101: } catch (MalformedURLException e) {
102: fatal("System Property 'url' is not a valid URL: " + u);
103: }
104: final SimplePostTool t = new SimplePostTool(u);
105:
106: final String mode = System.getProperty("data",
107: DEFAULT_DATA_MODE);
108: if (!DATA_MODES.contains(mode)) {
109: fatal("System Property 'data' is not valid for this tool: "
110: + mode);
111: }
112:
113: try {
114: if (DATA_MODE_FILES.equals(mode)) {
115: if (0 < args.length) {
116: info("POSTing files to " + u + "..");
117: final int posted = t.postFiles(args, 0);
118: }
119:
120: } else if (DATA_MODE_ARGS.equals(mode)) {
121: if (0 < args.length) {
122: info("POSTing args to " + u + "..");
123: for (String a : args) {
124: final StringWriter sw = new StringWriter();
125: t.postData(new StringReader(a), sw);
126: warnIfNotExpectedResponse(sw.toString(),
127: SOLR_OK_RESPONSE_EXCERPT);
128: }
129: }
130:
131: } else if (DATA_MODE_STDIN.equals(mode)) {
132: info("POSTing stdin to " + u + "..");
133: final StringWriter sw = new StringWriter();
134: t.postData(new InputStreamReader(System.in,
135: POST_ENCODING), sw);
136: warnIfNotExpectedResponse(sw.toString(),
137: SOLR_OK_RESPONSE_EXCERPT);
138: }
139: if ("yes".equals(System.getProperty("commit",
140: DEFAULT_COMMIT))) {
141: info("COMMITting Solr index changes..");
142: final StringWriter sw = new StringWriter();
143: t.commit(sw);
144: warnIfNotExpectedResponse(sw.toString(),
145: SOLR_OK_RESPONSE_EXCERPT);
146: }
147:
148: } catch (IOException ioe) {
149: fatal("Unexpected IOException " + ioe);
150: }
151: }
152:
153: /** Post all filenames provided in args, return the number of files posted*/
154: int postFiles(String[] args, int startIndexInArgs)
155: throws IOException {
156: int filesPosted = 0;
157: for (int j = startIndexInArgs; j < args.length; j++) {
158: File srcFile = new File(args[j]);
159: final StringWriter sw = new StringWriter();
160:
161: if (srcFile.canRead()) {
162: info("POSTing file " + srcFile.getName());
163: postFile(srcFile, sw);
164: filesPosted++;
165: warnIfNotExpectedResponse(sw.toString(),
166: SOLR_OK_RESPONSE_EXCERPT);
167: } else {
168: warn("Cannot read input file: " + srcFile);
169: }
170: }
171: return filesPosted;
172: }
173:
174: /** Check what Solr replied to a POST, and complain if it's not what we expected.
175: * TODO: parse the response and check it XMLwise, here we just check it as an unparsed String
176: */
177: static void warnIfNotExpectedResponse(String actual, String expected) {
178: if (actual.indexOf(expected) < 0) {
179: warn("Unexpected response from Solr: '" + actual
180: + "' does not contain '" + expected + "'");
181: }
182: }
183:
184: static void warn(String msg) {
185: System.err.println("SimplePostTool: WARNING: " + msg);
186: }
187:
188: static void info(String msg) {
189: System.out.println("SimplePostTool: " + msg);
190: }
191:
192: static void fatal(String msg) {
193: System.err.println("SimplePostTool: FATAL: " + msg);
194: System.exit(1);
195: }
196:
197: /**
198: * Constructs an instance for posting data to the specified Solr URL
199: * (ie: "http://localhost:8983/solr/update")
200: */
201: public SimplePostTool(URL solrUrl) {
202: this .solrUrl = solrUrl;
203: warn("Make sure your XML documents are encoded in "
204: + POST_ENCODING
205: + ", other encodings are not currently supported");
206: }
207:
208: /**
209: * Does a simple commit operation
210: */
211: public void commit(Writer output) throws IOException {
212: postData(new StringReader("<commit/>"), output);
213: }
214:
215: /**
216: * Opens the file and posts it's contents to the solrUrl,
217: * writes to response to output.
218: * @throws UnsupportedEncodingException
219: */
220: public void postFile(File file, Writer output)
221: throws FileNotFoundException, UnsupportedEncodingException {
222:
223: // FIXME; use a real XML parser to read files, so as to support various encodings
224: // (and we can only post well-formed XML anyway)
225: Reader reader = new InputStreamReader(
226: new FileInputStream(file), POST_ENCODING);
227: try {
228: postData(reader, output);
229: } finally {
230: try {
231: if (reader != null)
232: reader.close();
233: } catch (IOException e) {
234: throw new PostException(
235: "IOException while closing file", e);
236: }
237: }
238: }
239:
240: /**
241: * Reads data from the data reader and posts it to solr,
242: * writes to the response to output
243: */
244: public void postData(Reader data, Writer output) {
245:
246: HttpURLConnection urlc = null;
247: try {
248: urlc = (HttpURLConnection) solrUrl.openConnection();
249: try {
250: urlc.setRequestMethod("POST");
251: } catch (ProtocolException e) {
252: throw new PostException(
253: "Shouldn't happen: HttpURLConnection doesn't support POST??",
254: e);
255: }
256: urlc.setDoOutput(true);
257: urlc.setDoInput(true);
258: urlc.setUseCaches(false);
259: urlc.setAllowUserInteraction(false);
260: urlc.setRequestProperty("Content-type",
261: "text/xml; charset=" + POST_ENCODING);
262:
263: OutputStream out = urlc.getOutputStream();
264:
265: try {
266: Writer writer = new OutputStreamWriter(out,
267: POST_ENCODING);
268: pipe(data, writer);
269: writer.close();
270: } catch (IOException e) {
271: throw new PostException(
272: "IOException while posting data", e);
273: } finally {
274: if (out != null)
275: out.close();
276: }
277:
278: InputStream in = urlc.getInputStream();
279: try {
280: Reader reader = new InputStreamReader(in);
281: pipe(reader, output);
282: reader.close();
283: } catch (IOException e) {
284: throw new PostException(
285: "IOException while reading response", e);
286: } finally {
287: if (in != null)
288: in.close();
289: }
290:
291: } catch (IOException e) {
292: fatal("Connection error (is Solr running at " + solrUrl
293: + " ?): " + e);
294:
295: } finally {
296: if (urlc != null)
297: urlc.disconnect();
298: }
299: }
300:
301: /**
302: * Pipes everything from the reader to the writer via a buffer
303: */
304: private static void pipe(Reader reader, Writer writer)
305: throws IOException {
306: char[] buf = new char[1024];
307: int read = 0;
308: while ((read = reader.read(buf)) >= 0) {
309: writer.write(buf, 0, read);
310: }
311: writer.flush();
312: }
313: }
|