start work on the groovy plugin

This commit is contained in:
kimchy 2010-04-14 08:34:47 +03:00
parent 913bc2f947
commit cb7e92b0f8
44 changed files with 1886 additions and 245 deletions

View File

@ -30,6 +30,7 @@
<w>jgroups</w> <w>jgroups</w>
<w>joda</w> <w>joda</w>
<w>jsonp</w> <w>jsonp</w>
<w>kimchy</w>
<w>lifecycle</w> <w>lifecycle</w>
<w>linefeeds</w> <w>linefeeds</w>
<w>lucene</w> <w>lucene</w>

View File

@ -6,6 +6,7 @@
<module fileurl="file://$PROJECT_DIR$/.idea/modules/elasticsearch.iml" filepath="$PROJECT_DIR$/.idea/modules/elasticsearch.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules/elasticsearch.iml" filepath="$PROJECT_DIR$/.idea/modules/elasticsearch.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/elasticsearch-root.iml" filepath="$PROJECT_DIR$/.idea/modules/elasticsearch-root.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules/elasticsearch-root.iml" filepath="$PROJECT_DIR$/.idea/modules/elasticsearch-root.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules//plugin-attachments.iml" filepath="$PROJECT_DIR$/.idea/modules//plugin-attachments.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules//plugin-attachments.iml" filepath="$PROJECT_DIR$/.idea/modules//plugin-attachments.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules//plugin-groovy.iml" filepath="$PROJECT_DIR$/.idea/modules//plugin-groovy.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules//test-integration.iml" filepath="$PROJECT_DIR$/.idea/modules//test-integration.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules//test-integration.iml" filepath="$PROJECT_DIR$/.idea/modules//test-integration.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules//test-testng.iml" filepath="$PROJECT_DIR$/.idea/modules//test-testng.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules//test-testng.iml" filepath="$PROJECT_DIR$/.idea/modules//test-testng.iml" />
</modules> </modules>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/../../plugins/groovy/build/classes/main" />
<output-test url="file://$MODULE_DIR$/../../plugins/groovy/build/classes/test" />
<exclude-output />
<content url="file://$MODULE_DIR$/../../plugins/groovy">
<sourceFolder url="file://$MODULE_DIR$/../../plugins/groovy/src/main/groovy" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../plugins/groovy/src/test/groovy" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="groovy-1.7.2" level="application" />
<orderEntry type="module" module-name="elasticsearch" />
<orderEntry type="library" scope="TEST" name="testng" level="project" />
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
<orderEntry type="module" module-name="test-testng" scope="TEST" />
</component>
</module>

View File

@ -18,6 +18,12 @@ explodedDistLibDir = new File(explodedDistDir, 'lib')
explodedDistBinDir = new File(explodedDistDir, 'bin') explodedDistBinDir = new File(explodedDistDir, 'bin')
explodedDistConfigDir = new File(explodedDistDir, 'config') explodedDistConfigDir = new File(explodedDistDir, 'config')
//mavenRepoUrl = "file://localhost/" + projectDir.absolutePath + "/build/maven/repository"
//mavenSnapshotRepoUrl = "file://localhost/" + projectDir.absolutePath + "/build/maven/snapshotRepository"
mavenRepoUrl = "http://oss.sonatype.org/service/local/staging/deploy/maven2/"
mavenSnapshotRepoUrl = "http://oss.sonatype.org/content/repositories/snapshots"
mavenRepoUser = "kimchy"
mavenRepoPass = System.getenv("REPO_PASS")
allprojects { allprojects {
group = 'org.elasticsearch' group = 'org.elasticsearch'

View File

@ -97,15 +97,12 @@ artifacts {
uploadArchives { uploadArchives {
repositories.mavenDeployer { repositories.mavenDeployer {
// repository(url: "file://localhost/" + rootProject.projectDir.absolutePath + "/build/maven/repository")
// snapshotRepository(url: "file://localhost/" + rootProject.projectDir.absolutePath + "/build/maven/snapshotRepository")
configuration = configurations.deployerJars configuration = configurations.deployerJars
repository(url: "http://oss.sonatype.org/service/local/staging/deploy/maven2/") { repository(url: rootProject.mavenRepoUrl) {
authentication(userName: "kimchy", password: System.getenv("REPO_PASS")) authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
} }
snapshotRepository(url: "http://oss.sonatype.org/content/repositories/snapshots") { snapshotRepository(url: rootProject.mavenSnapshotRepoUrl) {
authentication(userName: "kimchy", password: System.getenv("REPO_PASS")) authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
} }
pom.project { pom.project {

View File

@ -20,11 +20,19 @@
package org.elasticsearch.action; package org.elasticsearch.action;
/** /**
* @author kimchy (Shay Banon) * A listener for action responses or failures.
*
* @author kimchy (shay.banon)
*/ */
public interface ActionListener<Response> { public interface ActionListener<Response> {
/**
* A response handler.
*/
void onResponse(Response response); void onResponse(Response response);
/**
* A failure handler.
*/
void onFailure(Throwable e); void onFailure(Throwable e);
} }

View File

@ -22,7 +22,7 @@ package org.elasticsearch.action;
import org.elasticsearch.util.io.stream.Streamable; import org.elasticsearch.util.io.stream.Streamable;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (shay.banon)
*/ */
public interface ActionRequest extends Streamable { public interface ActionRequest extends Streamable {

View File

@ -22,7 +22,7 @@ package org.elasticsearch.action;
import org.elasticsearch.util.io.stream.Streamable; import org.elasticsearch.util.io.stream.Streamable;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (shay.banon)
*/ */
public interface ActionResponse extends Streamable { public interface ActionResponse extends Streamable {
} }

View File

@ -0,0 +1,38 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.action;
/**
* An {@link ActionFuture} that listeners can be added to.
*
* @author kimchy (shay.banon)
*/
public interface ListenableActionFuture<T> extends ActionFuture<T> {
/**
* Add an action listener to be invoked when a response has received.
*/
void addListener(final ActionListener<T> listener);
/**
* Add an action listener (runnable) to be invoked when a response has received.
*/
void addListener(final Runnable listener);
}

View File

@ -58,6 +58,13 @@ public class DeleteResponse implements ActionResponse, Streamable {
return this.index; return this.index;
} }
/**
* The index the document was deleted from.
*/
public String getIndex() {
return index;
}
/** /**
* The type of the document deleted. * The type of the document deleted.
*/ */
@ -65,6 +72,13 @@ public class DeleteResponse implements ActionResponse, Streamable {
return this.type; return this.type;
} }
/**
* The type of the document deleted.
*/
public String getType() {
return type;
}
/** /**
* The id of the document deleted. * The id of the document deleted.
*/ */
@ -72,6 +86,13 @@ public class DeleteResponse implements ActionResponse, Streamable {
return this.id; return this.id;
} }
/**
* The id of the document deleted.
*/
public String getId() {
return id;
}
@Override public void readFrom(StreamInput in) throws IOException { @Override public void readFrom(StreamInput in) throws IOException {
index = in.readUTF(); index = in.readUTF();
id = in.readUTF(); id = in.readUTF();

View File

@ -50,10 +50,18 @@ public class GetField implements Streamable, Iterable<Object> {
return name; return name;
} }
public String getName() {
return name;
}
public List<Object> values() { public List<Object> values() {
return values; return values;
} }
public List<Object> getValues() {
return values;
}
@Override public Iterator<Object> iterator() { @Override public Iterator<Object> iterator() {
return values.iterator(); return values.iterator();
} }

View File

@ -55,6 +55,8 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
private Map<String, GetField> fields; private Map<String, GetField> fields;
private Map<String, Object> sourceAsMap;
private byte[] source; private byte[] source;
GetResponse() { GetResponse() {
@ -79,6 +81,13 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
return exists; return exists;
} }
/**
* Does the document exists.
*/
public boolean isExists() {
return exists;
}
/** /**
* The index the document was fetched from. * The index the document was fetched from.
*/ */
@ -86,6 +95,13 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
return this.index; return this.index;
} }
/**
* The index the document was fetched from.
*/
public String getIndex() {
return index;
}
/** /**
* The type of the document. * The type of the document.
*/ */
@ -93,6 +109,13 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
return type; return type;
} }
/**
* The type of the document.
*/
public String getType() {
return type;
}
/** /**
* The id of the document. * The id of the document.
*/ */
@ -100,6 +123,13 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
return id; return id;
} }
/**
* The id of the document.
*/
public String getId() {
return id;
}
/** /**
* The source of the document if exists. * The source of the document if exists.
*/ */
@ -125,17 +155,29 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
if (source == null) { if (source == null) {
return null; return null;
} }
if (sourceAsMap != null) {
return sourceAsMap;
}
try { try {
return defaultObjectMapper().readValue(source, 0, source.length, Map.class); sourceAsMap = defaultObjectMapper().readValue(source, 0, source.length, Map.class);
return sourceAsMap;
} catch (Exception e) { } catch (Exception e) {
throw new ElasticSearchParseException("Failed to parse source to map", e); throw new ElasticSearchParseException("Failed to parse source to map", e);
} }
} }
public Map<String, Object> getSource() {
return sourceAsMap();
}
public Map<String, GetField> fields() { public Map<String, GetField> fields() {
return this.fields; return this.fields;
} }
public Map<String, GetField> getFields() {
return fields;
}
public GetField field(String name) { public GetField field(String name) {
return fields.get(name); return fields.get(name);
} }

View File

@ -104,6 +104,9 @@ public class IndexRequest extends ShardReplicationOperationRequest {
private byte[] source; private byte[] source;
private OpType opType = OpType.INDEX; private OpType opType = OpType.INDEX;
public IndexRequest() {
}
/** /**
* Constructs a new index request against the specific index. The {@link #type(String)}, * Constructs a new index request against the specific index. The {@link #type(String)},
* {@link #id(String)} and {@link #source(byte[])} must be set. * {@link #id(String)} and {@link #source(byte[])} must be set.
@ -127,9 +130,6 @@ public class IndexRequest extends ShardReplicationOperationRequest {
this.source = source; this.source = source;
} }
IndexRequest() {
}
@Override public ActionRequestValidationException validate() { @Override public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = super.validate(); ActionRequestValidationException validationException = super.validate();
if (type == null) { if (type == null) {

View File

@ -58,6 +58,13 @@ public class IndexResponse implements ActionResponse, Streamable {
return this.index; return this.index;
} }
/**
* The index the document was indexed into.
*/
public String getIndex() {
return index;
}
/** /**
* The type of the document indexed. * The type of the document indexed.
*/ */
@ -65,6 +72,13 @@ public class IndexResponse implements ActionResponse, Streamable {
return this.type; return this.type;
} }
/**
* The type of the document indexed.
*/
public String getType() {
return type;
}
/** /**
* The id of the document indexed. * The id of the document indexed.
*/ */
@ -72,6 +86,13 @@ public class IndexResponse implements ActionResponse, Streamable {
return this.id; return this.id;
} }
/**
* The id of the document indexed.
*/
public String getId() {
return id;
}
@Override public void readFrom(StreamInput in) throws IOException { @Override public void readFrom(StreamInput in) throws IOException {
index = in.readUTF(); index = in.readUTF();
id = in.readUTF(); id = in.readUTF();

View File

@ -0,0 +1,128 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.action.support;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.threadpool.ThreadPool;
import java.util.List;
import static com.google.common.collect.Lists.*;
/**
* @author kimchy (shay.banon)
*/
public abstract class AbstractListenableActionFuture<T, L> extends AdapterActionFuture<T, L> {
private final boolean listenerThreaded;
private final ThreadPool threadPool;
private volatile Object listeners;
private boolean executedListeners = false;
protected AbstractListenableActionFuture(boolean listenerThreaded, ThreadPool threadPool) {
this.listenerThreaded = listenerThreaded;
this.threadPool = threadPool;
}
public void addListener(final ActionListener<T> listener) {
internalAddListener(listener);
}
public void addListener(final Runnable listener) {
internalAddListener(listener);
}
public void internalAddListener(Object listener) {
boolean executeImmediate = false;
synchronized (this) {
if (executedListeners) {
executeImmediate = true;
} else {
Object listeners = this.listeners;
if (listeners == null) {
listeners = listener;
} else if (listeners instanceof List) {
((List) this.listeners).add(listener);
} else {
Object orig = listeners;
listeners = newArrayListWithExpectedSize(2);
((List) listeners).add(orig);
((List) listeners).add(listener);
}
this.listeners = listeners;
}
}
if (executeImmediate) {
executeListener(listener);
}
}
@Override protected void done() {
super.done();
synchronized (this) {
executedListeners = true;
}
Object listeners = this.listeners;
if (listeners != null) {
if (listeners instanceof List) {
List list = (List) listeners;
for (Object listener : list) {
executeListener(listener);
}
} else {
executeListener(listeners);
}
}
}
private void executeListener(final Object listener) {
if (listenerThreaded) {
if (listener instanceof Runnable) {
threadPool.execute((Runnable) listener);
} else {
threadPool.execute(new Runnable() {
@Override public void run() {
ActionListener<T> lst = (ActionListener<T>) listener;
try {
lst.onResponse(actionGet());
} catch (ElasticSearchException e) {
lst.onFailure(e);
}
}
});
}
} else {
if (listener instanceof Runnable) {
((Runnable) listener).run();
} else {
ActionListener<T> lst = (ActionListener<T>) listener;
try {
lst.onResponse(actionGet());
} catch (ElasticSearchException e) {
lst.onFailure(e);
}
}
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.action.support;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchInterruptedException;
import org.elasticsearch.ElasticSearchTimeoutException;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.transport.TransportException;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.concurrent.AbstractFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author kimchy (shay.banon)
*/
public abstract class AdapterActionFuture<T, L> extends AbstractFuture<T> implements ActionFuture<T>, ActionListener<L> {
@Override public T actionGet() throws ElasticSearchException {
try {
return get();
} catch (InterruptedException e) {
throw new ElasticSearchInterruptedException(e.getMessage());
} catch (ExecutionException e) {
if (e.getCause() instanceof ElasticSearchException) {
throw (ElasticSearchException) e.getCause();
} else {
throw new TransportException("Failed execution", e);
}
}
}
@Override public T actionGet(String timeout) throws ElasticSearchException {
return actionGet(TimeValue.parseTimeValue(timeout, null));
}
@Override public T actionGet(long timeoutMillis) throws ElasticSearchException {
return actionGet(timeoutMillis, TimeUnit.MILLISECONDS);
}
@Override public T actionGet(TimeValue timeout) throws ElasticSearchException {
return actionGet(timeout.millis(), TimeUnit.MILLISECONDS);
}
@Override public T actionGet(long timeout, TimeUnit unit) throws ElasticSearchException {
try {
return get(timeout, unit);
} catch (TimeoutException e) {
throw new ElasticSearchTimeoutException(e.getMessage());
} catch (InterruptedException e) {
throw new ElasticSearchInterruptedException(e.getMessage());
} catch (ExecutionException e) {
if (e.getCause() instanceof ElasticSearchException) {
throw (ElasticSearchException) e.getCause();
} else {
throw new ElasticSearchException("Failed execution", e);
}
}
}
@Override public void onResponse(L result) {
set(convert(result));
}
@Override public void onFailure(Throwable e) {
setException(e);
}
protected abstract T convert(L listenerResponse);
}

View File

@ -19,143 +19,16 @@
package org.elasticsearch.action.support; package org.elasticsearch.action.support;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchInterruptedException;
import org.elasticsearch.ElasticSearchTimeoutException;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.transport.TransportException;
import org.elasticsearch.util.TimeValue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
*/ */
public class PlainActionFuture<T> implements ActionFuture<T>, ActionListener<T> { public class PlainActionFuture<T> extends AdapterActionFuture<T, T> {
public static <T> PlainActionFuture<T> newFuture() { public static <T> PlainActionFuture<T> newFuture() {
return new PlainActionFuture<T>(); return new PlainActionFuture<T>();
} }
private final CountDownLatch latch; @Override protected T convert(T listenerResponse) {
return listenerResponse;
private volatile boolean done;
private volatile boolean canceled;
private volatile T result;
private volatile Throwable exp;
public PlainActionFuture() {
latch = new CountDownLatch(1);
}
@Override public boolean cancel(boolean mayInterruptIfRunning) {
if (done)
return true;
canceled = true;
latch.countDown();
return true;
}
@Override public boolean isCancelled() {
return canceled;
}
@Override public boolean isDone() {
return done;
}
@Override public T get() throws InterruptedException, ExecutionException {
latch.await();
if (!done || canceled) {
throw new InterruptedException("future was interrupted");
}
if (exp != null) {
throw new ExecutionException(exp.getMessage(), exp);
}
return this.result;
}
@Override public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
latch.await(timeout, unit);
if (!done || canceled) {
throw new TimeoutException("response did not arrive");
}
if (exp != null) {
throw new ExecutionException(exp.getMessage(), exp);
}
return this.result;
}
@Override public T actionGet() throws ElasticSearchException {
try {
return get();
} catch (InterruptedException e) {
throw new ElasticSearchInterruptedException(e.getMessage());
} catch (ExecutionException e) {
if (e.getCause() instanceof ElasticSearchException) {
throw (ElasticSearchException) e.getCause();
} else {
throw new TransportException("Failed execution", e);
}
}
}
@Override public T actionGet(String timeout) throws ElasticSearchException {
return actionGet(TimeValue.parseTimeValue(timeout, null));
}
@Override public T actionGet(long timeoutMillis) throws ElasticSearchException {
return actionGet(timeoutMillis, TimeUnit.MILLISECONDS);
}
@Override public T actionGet(TimeValue timeout) throws ElasticSearchException {
return actionGet(timeout.millis(), TimeUnit.MILLISECONDS);
}
@Override public T actionGet(long timeout, TimeUnit unit) throws ElasticSearchException {
try {
return get(timeout, unit);
} catch (TimeoutException e) {
throw new ElasticSearchTimeoutException(e.getMessage());
} catch (InterruptedException e) {
throw new ElasticSearchInterruptedException(e.getMessage());
} catch (ExecutionException e) {
if (e.getCause() instanceof ElasticSearchException) {
throw (ElasticSearchException) e.getCause();
} else {
throw new ElasticSearchException("Failed execution", e);
}
}
}
@Override public void onResponse(T result) {
this.done = true;
this.result = result;
if (canceled)
return;
latch.countDown();
}
@Override public void onFailure(Throwable e) {
this.done = true;
this.exp = e;
if (canceled)
return;
latch.countDown();
} }
} }

View File

@ -0,0 +1,36 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.action.support;
import org.elasticsearch.threadpool.ThreadPool;
/**
* @author kimchy (shay.banon)
*/
public class PlainListenableActionFuture<T> extends AbstractListenableActionFuture<T, T> {
public PlainListenableActionFuture(boolean listenerThreaded, ThreadPool threadPool) {
super(listenerThreaded, threadPool);
}
@Override protected T convert(T response) {
return response;
}
}

View File

@ -66,6 +66,13 @@ public abstract class BroadcastOperationResponse implements ActionResponse {
return totalShards; return totalShards;
} }
/**
* The total shards this request ran against.
*/
public int getTotalShards() {
return totalShards;
}
/** /**
* The successful shards this request was executed on. * The successful shards this request was executed on.
*/ */
@ -73,6 +80,13 @@ public abstract class BroadcastOperationResponse implements ActionResponse {
return successfulShards; return successfulShards;
} }
/**
* The successful shards this request was executed on.
*/
public int getSuccessfulShards() {
return successfulShards;
}
/** /**
* The failed shards this request was executed on. * The failed shards this request was executed on.
*/ */
@ -80,6 +94,13 @@ public abstract class BroadcastOperationResponse implements ActionResponse {
return failedShards; return failedShards;
} }
/**
* The failed shards this request was executed on.
*/
public int getFailedShards() {
return failedShards;
}
/** /**
* The list of shard failures exception. * The list of shard failures exception.
*/ */
@ -90,6 +111,13 @@ public abstract class BroadcastOperationResponse implements ActionResponse {
return shardFailures; return shardFailures;
} }
/**
* The list of shard failures exception.
*/
public List<ShardOperationFailedException> getShardFailures() {
return shardFailures;
}
@Override public void readFrom(StreamInput in) throws IOException { @Override public void readFrom(StreamInput in) throws IOException {
totalShards = in.readVInt(); totalShards = in.readVInt();
successfulShards = in.readVInt(); successfulShards = in.readVInt();

View File

@ -53,6 +53,10 @@ import org.elasticsearch.action.terms.TermsRequest;
*/ */
public class Requests { public class Requests {
public static IndexRequest indexRequest() {
return new IndexRequest();
}
/** /**
* Create an index request against a specific index. Note the {@link IndexRequest#type(String)} must be * Create an index request against a specific index. Note the {@link IndexRequest#type(String)} must be
* set as well and optionally the {@link IndexRequest#id(String)}. * set as well and optionally the {@link IndexRequest#id(String)}.

View File

@ -0,0 +1,31 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.client.internal;
import org.elasticsearch.client.Client;
import org.elasticsearch.threadpool.ThreadPool;
/**
* @author kimchy (shay.banon)
*/
public interface InternalClient extends Client {
ThreadPool threadPool();
}

View File

@ -44,14 +44,17 @@ import org.elasticsearch.action.terms.TermsRequest;
import org.elasticsearch.action.terms.TermsResponse; import org.elasticsearch.action.terms.TermsResponse;
import org.elasticsearch.action.terms.TransportTermsAction; import org.elasticsearch.action.terms.TransportTermsAction;
import org.elasticsearch.client.AdminClient; import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client; import org.elasticsearch.client.internal.InternalClient;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.util.component.AbstractComponent; import org.elasticsearch.util.component.AbstractComponent;
import org.elasticsearch.util.settings.Settings; import org.elasticsearch.util.settings.Settings;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
*/ */
public class NodeClient extends AbstractComponent implements Client { public class NodeClient extends AbstractComponent implements InternalClient {
private final ThreadPool threadPool;
private final NodeAdminClient admin; private final NodeAdminClient admin;
@ -73,12 +76,13 @@ public class NodeClient extends AbstractComponent implements Client {
private final TransportMoreLikeThisAction moreLikeThisAction; private final TransportMoreLikeThisAction moreLikeThisAction;
@Inject public NodeClient(Settings settings, NodeAdminClient admin, @Inject public NodeClient(Settings settings, ThreadPool threadPool, NodeAdminClient admin,
TransportIndexAction indexAction, TransportDeleteAction deleteAction, TransportIndexAction indexAction, TransportDeleteAction deleteAction,
TransportDeleteByQueryAction deleteByQueryAction, TransportGetAction getAction, TransportCountAction countAction, TransportDeleteByQueryAction deleteByQueryAction, TransportGetAction getAction, TransportCountAction countAction,
TransportSearchAction searchAction, TransportSearchScrollAction searchScrollAction, TransportSearchAction searchAction, TransportSearchScrollAction searchScrollAction,
TransportTermsAction termsAction, TransportMoreLikeThisAction moreLikeThisAction) { TransportTermsAction termsAction, TransportMoreLikeThisAction moreLikeThisAction) {
super(settings); super(settings);
this.threadPool = threadPool;
this.admin = admin; this.admin = admin;
this.indexAction = indexAction; this.indexAction = indexAction;
this.deleteAction = deleteAction; this.deleteAction = deleteAction;
@ -91,6 +95,10 @@ public class NodeClient extends AbstractComponent implements Client {
this.moreLikeThisAction = moreLikeThisAction; this.moreLikeThisAction = moreLikeThisAction;
} }
@Override public ThreadPool threadPool() {
return this.threadPool;
}
@Override public void close() { @Override public void close() {
// nothing really to do // nothing really to do
} }

View File

@ -43,7 +43,7 @@ import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.terms.TermsRequest; import org.elasticsearch.action.terms.TermsRequest;
import org.elasticsearch.action.terms.TermsResponse; import org.elasticsearch.action.terms.TermsResponse;
import org.elasticsearch.client.AdminClient; import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client; import org.elasticsearch.client.internal.InternalClient;
import org.elasticsearch.client.transport.action.ClientTransportActionModule; import org.elasticsearch.client.transport.action.ClientTransportActionModule;
import org.elasticsearch.client.transport.support.InternalTransportClient; import org.elasticsearch.client.transport.support.InternalTransportClient;
import org.elasticsearch.cluster.ClusterNameModule; import org.elasticsearch.cluster.ClusterNameModule;
@ -77,7 +77,7 @@ import static org.elasticsearch.util.settings.ImmutableSettings.*;
* *
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
*/ */
public class TransportClient implements Client { public class TransportClient implements InternalClient {
private final Injector injector; private final Injector injector;
@ -209,6 +209,10 @@ public class TransportClient implements Client {
ThreadLocals.clearReferencesThreadLocals(); ThreadLocals.clearReferencesThreadLocals();
} }
@Override public ThreadPool threadPool() {
return internalClient.threadPool();
}
@Override public AdminClient admin() { @Override public AdminClient admin() {
return internalClient.admin(); return internalClient.admin();
} }

View File

@ -40,7 +40,7 @@ import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.terms.TermsRequest; import org.elasticsearch.action.terms.TermsRequest;
import org.elasticsearch.action.terms.TermsResponse; import org.elasticsearch.action.terms.TermsResponse;
import org.elasticsearch.client.AdminClient; import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client; import org.elasticsearch.client.internal.InternalClient;
import org.elasticsearch.client.transport.TransportClientNodesService; import org.elasticsearch.client.transport.TransportClientNodesService;
import org.elasticsearch.client.transport.action.count.ClientTransportCountAction; import org.elasticsearch.client.transport.action.count.ClientTransportCountAction;
import org.elasticsearch.client.transport.action.delete.ClientTransportDeleteAction; import org.elasticsearch.client.transport.action.delete.ClientTransportDeleteAction;
@ -52,13 +52,16 @@ import org.elasticsearch.client.transport.action.search.ClientTransportSearchAct
import org.elasticsearch.client.transport.action.search.ClientTransportSearchScrollAction; import org.elasticsearch.client.transport.action.search.ClientTransportSearchScrollAction;
import org.elasticsearch.client.transport.action.terms.ClientTransportTermsAction; import org.elasticsearch.client.transport.action.terms.ClientTransportTermsAction;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.util.component.AbstractComponent; import org.elasticsearch.util.component.AbstractComponent;
import org.elasticsearch.util.settings.Settings; import org.elasticsearch.util.settings.Settings;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (Shay Banon)
*/ */
public class InternalTransportClient extends AbstractComponent implements Client { public class InternalTransportClient extends AbstractComponent implements InternalClient {
private final ThreadPool threadPool;
private final TransportClientNodesService nodesService; private final TransportClientNodesService nodesService;
@ -82,12 +85,14 @@ public class InternalTransportClient extends AbstractComponent implements Client
private final ClientTransportMoreLikeThisAction moreLikeThisAction; private final ClientTransportMoreLikeThisAction moreLikeThisAction;
@Inject public InternalTransportClient(Settings settings, TransportClientNodesService nodesService, InternalTransportAdminClient adminClient, @Inject public InternalTransportClient(Settings settings, ThreadPool threadPool,
TransportClientNodesService nodesService, InternalTransportAdminClient adminClient,
ClientTransportIndexAction indexAction, ClientTransportDeleteAction deleteAction, ClientTransportGetAction getAction, ClientTransportIndexAction indexAction, ClientTransportDeleteAction deleteAction, ClientTransportGetAction getAction,
ClientTransportDeleteByQueryAction deleteByQueryAction, ClientTransportCountAction countAction, ClientTransportDeleteByQueryAction deleteByQueryAction, ClientTransportCountAction countAction,
ClientTransportSearchAction searchAction, ClientTransportSearchScrollAction searchScrollAction, ClientTransportSearchAction searchAction, ClientTransportSearchScrollAction searchScrollAction,
ClientTransportTermsAction termsAction, ClientTransportMoreLikeThisAction moreLikeThisAction) { ClientTransportTermsAction termsAction, ClientTransportMoreLikeThisAction moreLikeThisAction) {
super(settings); super(settings);
this.threadPool = threadPool;
this.nodesService = nodesService; this.nodesService = nodesService;
this.adminClient = adminClient; this.adminClient = adminClient;
@ -106,6 +111,10 @@ public class InternalTransportClient extends AbstractComponent implements Client
// nothing to do here // nothing to do here
} }
@Override public ThreadPool threadPool() {
return this.threadPool;
}
@Override public AdminClient admin() { @Override public AdminClient admin() {
return adminClient; return adminClient;
} }

View File

@ -21,73 +21,22 @@ package org.elasticsearch.transport;
import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchInterruptedException; import org.elasticsearch.ElasticSearchInterruptedException;
import org.elasticsearch.util.concurrent.AbstractFuture;
import org.elasticsearch.util.io.stream.Streamable; import org.elasticsearch.util.io.stream.Streamable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (shay.banon)
*/ */
public class PlainTransportFuture<V extends Streamable> implements TransportFuture<V>, TransportResponseHandler<V> { public class PlainTransportFuture<V extends Streamable> extends AbstractFuture<V> implements TransportFuture<V>, TransportResponseHandler<V> {
private final CountDownLatch latch;
private final TransportResponseHandler<V> handler; private final TransportResponseHandler<V> handler;
private volatile boolean done;
private volatile boolean canceled;
private volatile V result;
private volatile Exception exp;
public PlainTransportFuture(TransportResponseHandler<V> handler) { public PlainTransportFuture(TransportResponseHandler<V> handler) {
this.handler = handler; this.handler = handler;
latch = new CountDownLatch(1);
}
@Override public boolean cancel(boolean mayInterruptIfRunning) {
if (done)
return true;
canceled = true;
latch.countDown();
return true;
}
@Override public boolean isCancelled() {
return canceled;
}
@Override public boolean isDone() {
return done;
}
@Override public V get() throws InterruptedException, ExecutionException {
latch.await();
if (!done || canceled) {
throw new InterruptedException("future was interrupted");
}
if (exp != null) {
throw new ExecutionException(exp.getMessage(), exp);
}
return this.result;
}
@Override public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
latch.await(timeout, unit);
if (!done || canceled) {
throw new TimeoutException("response did not arrive");
}
if (exp != null) {
throw new ExecutionException(exp.getMessage(), exp);
}
return this.result;
} }
@Override public V txGet() throws ElasticSearchException { @Override public V txGet() throws ElasticSearchException {
@ -123,25 +72,13 @@ public class PlainTransportFuture<V extends Streamable> implements TransportFutu
} }
@Override public void handleResponse(V response) { @Override public void handleResponse(V response) {
this.done = true;
this.result = response;
if (canceled)
return;
handler.handleResponse(response); handler.handleResponse(response);
latch.countDown(); set(response);
} }
@Override public void handleException(RemoteTransportException exp) { @Override public void handleException(RemoteTransportException exp) {
this.done = true;
this.exp = exp;
if (canceled)
return;
handler.handleException(exp); handler.handleException(exp);
latch.countDown(); setException(exp);
} }
@Override public boolean spawn() { @Override public boolean spawn() {

View File

@ -0,0 +1,328 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.concurrent;
import java.util.concurrent.*;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* <p>An abstract implementation of the {@link Future} interface. This class
* is an abstraction of {@link java.util.concurrent.FutureTask} to support use
* for tasks other than {@link Runnable}s. It uses an
* {@link AbstractQueuedSynchronizer} to deal with concurrency issues and
* guarantee thread safety. It could be used as a base class to
* {@code FutureTask}, or any other implementor of the {@code Future} interface.
*
* <p>This class implements all methods in {@code Future}. Subclasses should
* provide a way to set the result of the computation through the protected
* methods {@link #set(Object)}, {@link #setException(Throwable)}, or
* {@link #cancel()}. If subclasses want to implement cancellation they can
* override the {@link #cancel(boolean)} method with a real implementation, the
* default implementation doesn't support cancellation.
*
* <p>The state changing methods all return a boolean indicating success or
* failure in changing the future's state. Valid states are running,
* completed, failed, or cancelled. Because this class does not implement
* cancellation it is left to the subclass to distinguish between created
* and running tasks.
*/
public abstract class AbstractFuture<V> implements Future<V> {
/**
* Synchronization control for AbstractFutures.
*/
private final Sync<V> sync = new Sync<V>();
/*
* Blocks until either the task completes or the timeout expires. Uses the
* sync blocking-with-timeout support provided by AQS.
*/
public V get(long timeout, TimeUnit unit) throws InterruptedException,
TimeoutException, ExecutionException {
return sync.get(unit.toNanos(timeout));
}
/*
* Blocks until the task completes or we get interrupted. Uses the
* interruptible blocking support provided by AQS.
*/
public V get() throws InterruptedException, ExecutionException {
return sync.get();
}
/*
* Checks if the sync is not in the running state.
*/
public boolean isDone() {
return sync.isDone();
}
/*
* Checks if the sync is in the cancelled state.
*/
public boolean isCancelled() {
return sync.isCancelled();
}
/*
* Default implementation of cancel that never cancels the future.
* Subclasses should override this to implement cancellation if desired.
*/
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
/**
* Subclasses should invoke this method to set the result of the computation
* to {@code value}. This will set the state of the future to
* {@link AbstractFuture.Sync#COMPLETED} and call {@link #done()} if the
* state was successfully changed.
*
* @param value the value that was the result of the task.
* @return true if the state was successfully changed.
*/
protected boolean set(V value) {
boolean result = sync.set(value);
if (result) {
done();
}
return result;
}
/**
* Subclasses should invoke this method to set the result of the computation
* to an error, {@code throwable}. This will set the state of the future to
* {@link AbstractFuture.Sync#COMPLETED} and call {@link #done()} if the
* state was successfully changed.
*
* @param throwable the exception that the task failed with.
* @return true if the state was successfully changed.
* @throws Error if the throwable was an {@link Error}.
*/
protected boolean setException(Throwable throwable) {
boolean result = sync.setException(throwable);
if (result) {
done();
}
// If it's an Error, we want to make sure it reaches the top of the
// call stack, so we rethrow it.
if (throwable instanceof Error) {
throw (Error) throwable;
}
return result;
}
/**
* Subclasses should invoke this method to mark the future as cancelled.
* This will set the state of the future to {@link
* AbstractFuture.Sync#CANCELLED} and call {@link #done()} if the state was
* successfully changed.
*
* @return true if the state was successfully changed.
*/
protected final boolean cancel() {
boolean result = sync.cancel();
if (result) {
done();
}
return result;
}
/*
* Called by the success, failed, or cancelled methods to indicate that the
* value is now available and the latch can be released. Subclasses can
* use this method to deal with any actions that should be undertaken when
* the task has completed.
*/
protected void done() {
// Default implementation does nothing.
}
/**
* <p>Following the contract of {@link AbstractQueuedSynchronizer} we create a
* private subclass to hold the synchronizer. This synchronizer is used to
* implement the blocking and waiting calls as well as to handle state changes
* in a thread-safe manner. The current state of the future is held in the
* Sync state, and the lock is released whenever the state changes to either
* {@link #COMPLETED} or {@link #CANCELLED}.
*
* <p>To avoid races between threads doing release and acquire, we transition
* to the final state in two steps. One thread will successfully CAS from
* RUNNING to COMPLETING, that thread will then set the result of the
* computation, and only then transition to COMPLETED or CANCELLED.
*
* <p>We don't use the integer argument passed between acquire methods so we
* pass around a -1 everywhere.
*/
static final class Sync<V> extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 0L;
/* Valid states. */
static final int RUNNING = 0;
static final int COMPLETING = 1;
static final int COMPLETED = 2;
static final int CANCELLED = 4;
private V value;
private ExecutionException exception;
/*
* Acquisition succeeds if the future is done, otherwise it fails.
*/
@Override
protected int tryAcquireShared(int ignored) {
if (isDone()) {
return 1;
}
return -1;
}
/*
* We always allow a release to go through, this means the state has been
* successfully changed and the result is available.
*/
@Override
protected boolean tryReleaseShared(int finalState) {
setState(finalState);
return true;
}
/**
* Blocks until the task is complete or the timeout expires. Throws a
* {@link TimeoutException} if the timer expires, otherwise behaves like
* {@link #get()}.
*/
V get(long nanos) throws TimeoutException, CancellationException,
ExecutionException, InterruptedException {
// Attempt to acquire the shared lock with a timeout.
if (!tryAcquireSharedNanos(-1, nanos)) {
throw new TimeoutException("Timeout waiting for task.");
}
return getValue();
}
/**
* Blocks until {@link #complete(Object, Throwable, int)} has been
* successfully called. Throws a {@link CancellationException} if the task
* was cancelled, or a {@link ExecutionException} if the task completed with
* an error.
*/
V get() throws CancellationException, ExecutionException,
InterruptedException {
// Acquire the shared lock allowing interruption.
acquireSharedInterruptibly(-1);
return getValue();
}
/**
* Implementation of the actual value retrieval. Will return the value
* on success, an exception on failure, a cancellation on cancellation, or
* an illegal state if the synchronizer is in an invalid state.
*/
private V getValue() throws CancellationException, ExecutionException {
int state = getState();
switch (state) {
case COMPLETED:
if (exception != null) {
throw exception;
} else {
return value;
}
case CANCELLED:
throw new CancellationException("Task was cancelled.");
default:
throw new IllegalStateException(
"Error, synchronizer in invalid state: " + state);
}
}
/**
* Checks if the state is {@link #COMPLETED} or {@link #CANCELLED}.
*/
boolean isDone() {
return (getState() & (COMPLETED | CANCELLED)) != 0;
}
/**
* Checks if the state is {@link #CANCELLED}.
*/
boolean isCancelled() {
return getState() == CANCELLED;
}
/**
* Transition to the COMPLETED state and set the value.
*/
boolean set(V v) {
return complete(v, null, COMPLETED);
}
/**
* Transition to the COMPLETED state and set the exception.
*/
boolean setException(Throwable t) {
return complete(null, t, COMPLETED);
}
/**
* Transition to the CANCELLED state.
*/
boolean cancel() {
return complete(null, null, CANCELLED);
}
/**
* Implementation of completing a task. Either {@code v} or {@code t} will
* be set but not both. The {@code finalState} is the state to change to
* from {@link #RUNNING}. If the state is not in the RUNNING state we
* return {@code false}.
*
* @param v the value to set as the result of the computation.
* @param t the exception to set as the result of the computation.
* @param finalState the state to transition to.
*/
private boolean complete(V v, Throwable t, int finalState) {
if (compareAndSetState(RUNNING, COMPLETING)) {
this.value = v;
this.exception = t == null ? null : new ExecutionException(t);
releaseShared(finalState);
return true;
}
// The state was not RUNNING, so there are no valid transitions.
return false;
}
}
}

View File

@ -65,8 +65,6 @@ public class BinaryJsonBuilder extends JsonBuilder<BinaryJsonBuilder> {
private final JsonFactory factory; private final JsonFactory factory;
private StringBuilder cachedStringBuilder;
public BinaryJsonBuilder() throws IOException { public BinaryJsonBuilder() throws IOException {
this(Jackson.defaultJsonFactory()); this(Jackson.defaultJsonFactory());
} }
@ -85,10 +83,6 @@ public class BinaryJsonBuilder extends JsonBuilder<BinaryJsonBuilder> {
this.builder = this; this.builder = this;
} }
@Override protected StringBuilder cachedStringBuilder() {
return cachedStringBuilder;
}
@Override public BinaryJsonBuilder raw(byte[] json) throws IOException { @Override public BinaryJsonBuilder raw(byte[] json) throws IOException {
flush(); flush();
bos.write(json); bos.write(json);

View File

@ -69,6 +69,8 @@ public abstract class JsonBuilder<T extends JsonBuilder> {
protected FieldCaseConversion fieldCaseConversion = globalFieldCaseConversion; protected FieldCaseConversion fieldCaseConversion = globalFieldCaseConversion;
protected StringBuilder cachedStringBuilder;
public static StringJsonBuilder stringJsonBuilder() throws IOException { public static StringJsonBuilder stringJsonBuilder() throws IOException {
return StringJsonBuilder.Cached.cached(); return StringJsonBuilder.Cached.cached();
} }
@ -143,9 +145,9 @@ public abstract class JsonBuilder<T extends JsonBuilder> {
public T field(String name) throws IOException { public T field(String name) throws IOException {
if (fieldCaseConversion == FieldCaseConversion.UNDERSCORE) { if (fieldCaseConversion == FieldCaseConversion.UNDERSCORE) {
name = Strings.toUnderscoreCase(name); name = Strings.toUnderscoreCase(name, cachedStringBuilder);
} else if (fieldCaseConversion == FieldCaseConversion.CAMELCASE) { } else if (fieldCaseConversion == FieldCaseConversion.CAMELCASE) {
name = Strings.toCamelCase(name); name = Strings.toCamelCase(name, cachedStringBuilder);
} }
generator.writeFieldName(name); generator.writeFieldName(name);
return builder; return builder;
@ -403,10 +405,6 @@ public abstract class JsonBuilder<T extends JsonBuilder> {
public abstract String string() throws IOException; public abstract String string() throws IOException;
protected StringBuilder cachedStringBuilder() {
return null;
}
public void close() { public void close() {
try { try {
generator.close(); generator.close();

View File

@ -69,8 +69,6 @@ public class StringJsonBuilder extends JsonBuilder<StringJsonBuilder> {
final UnicodeUtil.UTF8Result utf8Result = new UnicodeUtil.UTF8Result(); final UnicodeUtil.UTF8Result utf8Result = new UnicodeUtil.UTF8Result();
private StringBuilder cachedStringBuilder;
public StringJsonBuilder() throws IOException { public StringJsonBuilder() throws IOException {
this(Jackson.defaultJsonFactory()); this(Jackson.defaultJsonFactory());
} }
@ -89,10 +87,6 @@ public class StringJsonBuilder extends JsonBuilder<StringJsonBuilder> {
this.builder = this; this.builder = this;
} }
@Override protected StringBuilder cachedStringBuilder() {
return cachedStringBuilder;
}
@Override public StringJsonBuilder raw(byte[] json) throws IOException { @Override public StringJsonBuilder raw(byte[] json) throws IOException {
flush(); flush();
Unicode.UTF16Result result = Unicode.unsafeFromBytesAsUtf16(json); Unicode.UTF16Result result = Unicode.unsafeFromBytesAsUtf16(json);

View File

@ -107,15 +107,12 @@ artifacts {
uploadArchives { uploadArchives {
repositories.mavenDeployer { repositories.mavenDeployer {
// repository(url: "file://localhost/" + rootProject.projectDir.absolutePath + "/build/maven/repository")
// snapshotRepository(url: "file://localhost/" + rootProject.projectDir.absolutePath + "/build/maven/snapshotRepository")
configuration = configurations.deployerJars configuration = configurations.deployerJars
repository(url: "http://oss.sonatype.org/service/local/staging/deploy/maven2/") { repository(url: rootProject.mavenRepoUrl) {
authentication(userName: "kimchy", password: System.getenv("REPO_PASS")) authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
} }
snapshotRepository(url: "http://oss.sonatype.org/content/repositories/snapshots") { snapshotRepository(url: rootProject.mavenSnapshotRepoUrl) {
authentication(userName: "kimchy", password: System.getenv("REPO_PASS")) authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
} }
pom.project { pom.project {

138
plugins/groovy/build.gradle Normal file
View File

@ -0,0 +1,138 @@
dependsOn(':elasticsearch')
apply plugin: 'groovy'
apply plugin: 'maven'
archivesBaseName = "elasticsearch-groovy"
explodedDistDir = new File(distsDir, 'exploded')
manifest.mainAttributes("Implementation-Title": "ElasticSearch::Plugins::Groovy", "Implementation-Version": rootProject.version, "Implementation-Date": buildTimeStr)
configurations.compile.transitive = true
configurations.testCompile.transitive = true
// no need to use the resource dir
sourceSets.main.resources.srcDir 'src/main/groovy'
sourceSets.test.resources.srcDir 'src/test/groovy'
// add the source files to the dist jar
jar {
from sourceSets.main.allSource
}
configurations {
dists
distLib {
visible = false
transitive = false
}
}
dependencies {
compile project(':elasticsearch')
groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.2'
testCompile project(':test-testng')
testCompile('org.testng:testng:5.10:jdk15') { transitive = false }
testCompile 'org.hamcrest:hamcrest-all:1.1'
}
test {
useTestNG()
jmvArgs = ["-ea", "-Xmx1024m"]
suiteName = project.name
listeners = ["org.elasticsearch.util.testng.Listeners"]
systemProperties["es.test.log.conf"] = System.getProperty("es.test.log.conf", "log4j-gradle.properties")
}
task explodedDist(dependsOn: [jar], description: 'Builds the plugin zip file') << {
[explodedDistDir]*.mkdirs()
copy {
from configurations.distLib
into explodedDistDir
}
// remove elasticsearch files (compile above adds the elasticsearch one)
ant.delete { fileset(dir: explodedDistDir, includes: "elasticsearch-*.jar") }
copy {
from libsDir
into explodedDistDir
}
ant.delete { fileset(dir: explodedDistDir, includes: "elasticsearch-*-javadoc.jar") }
ant.delete { fileset(dir: explodedDistDir, includes: "elasticsearch-*-sources.jar") }
}
task zip(type: Zip, dependsOn: ['explodedDist']) {
from(explodedDistDir) {
}
}
task release(dependsOn: [zip]) << {
ant.delete(dir: explodedDistDir)
copy {
from distsDir
into(new File(rootProject.distsDir, "plugins"))
}
}
configurations {
deployerJars
}
dependencies {
deployerJars "org.apache.maven.wagon:wagon-http:1.0-beta-2"
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives sourcesJar
archives javadocJar
}
uploadArchives {
repositories.mavenDeployer {
configuration = configurations.deployerJars
repository(url: rootProject.mavenRepoUrl) {
authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
}
snapshotRepository(url: rootProject.mavenSnapshotRepoUrl) {
authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
}
pom.project {
inceptionYear '2009'
name 'elasticsearch-plugins-groovy'
description 'Groovy Plugin for ElasticSearch'
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
scm {
connection 'git://github.com/elasticsearch/elasticsearch.git'
developerConnection 'git@github.com:elasticsearch/elasticsearch.git'
url 'http://github.com/elasticsearch/elasticsearch'
}
}
pom.whenConfigured {pom ->
pom.dependencies = pom.dependencies.findAll {dep -> dep.scope != 'test' } // removes the test scoped ones
}
}
}

View File

@ -0,0 +1,22 @@
package org.elasticsearch.groovy.client
import org.elasticsearch.client.internal.InternalClient
/**
* @author kimchy (shay.banon)
*/
class GAdminClient {
private final InternalClient internalClient;
final GIndicesAdminClient indices;
final GClusterAdminClient cluster;
def GAdminClient(internalClient) {
this.internalClient = internalClient;
this.indices = new GIndicesAdminClient(internalClient)
this.cluster = new GClusterAdminClient(internalClient)
}
}

View File

@ -0,0 +1,111 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.groovy.client
import org.elasticsearch.action.ActionListener
import org.elasticsearch.action.delete.DeleteRequest
import org.elasticsearch.action.delete.DeleteResponse
import org.elasticsearch.action.get.GetRequest
import org.elasticsearch.action.get.GetResponse
import org.elasticsearch.action.index.IndexRequest
import org.elasticsearch.action.index.IndexResponse
import org.elasticsearch.client.Client
import org.elasticsearch.client.internal.InternalClient
import org.elasticsearch.groovy.client.action.GActionFuture
import org.elasticsearch.groovy.util.json.JsonBuilder
/**
* @author kimchy (shay.banon)
*/
class GClient {
static {
IndexRequest.metaClass.setSource = {Closure c ->
delegate.source(new JsonBuilder().buildAsBytes(c))
}
IndexRequest.metaClass.source = {Closure c ->
delegate.source(new JsonBuilder().buildAsBytes(c))
}
}
final Client client;
private final InternalClient internalClient
final GAdminClient admin;
def GClient(client) {
this.client = client;
this.internalClient = client;
this.admin = new GAdminClient(internalClient)
}
GActionFuture<IndexResponse> index(Closure c) {
IndexRequest request = new IndexRequest()
c.setDelegate request
c.call()
index(request)
}
GActionFuture<IndexResponse> index(IndexRequest request) {
GActionFuture<IndexResponse> future = new GActionFuture<IndexResponse>(internalClient.threadPool(), request);
client.index(request, future)
return future
}
void index(IndexRequest request, ActionListener<IndexResponse> listener) {
client.index(request, listener)
}
GActionFuture<GetResponse> get(Closure c) {
GetRequest request = new GetRequest()
c.setDelegate request
c.call()
get(request)
}
GActionFuture<GetResponse> get(GetRequest request) {
GActionFuture<GetResponse> future = new GActionFuture<GetResponse>(internalClient.threadPool(), request);
client.get(request, future)
return future
}
void get(GetRequest request, ActionListener<GetResponse> listener) {
client.get(request, listener)
}
GActionFuture<DeleteResponse> delete(Closure c) {
DeleteRequest request = new DeleteRequest()
c.setDelegate request
c.call()
delete(request)
}
GActionFuture<DeleteResponse> delete(DeleteRequest request) {
GActionFuture<DeleteResponse> future = new GActionFuture<DeleteResponse>(internalClient.threadPool(), request);
client.delete(request, future)
return future
}
void delete(DeleteRequest request, ActionListener<DeleteResponse> listener) {
client.delete(request, listener)
}
}

View File

@ -0,0 +1,19 @@
package org.elasticsearch.groovy.client
import org.elasticsearch.client.ClusterAdminClient
import org.elasticsearch.client.internal.InternalClient
/**
* @author kimchy (shay.banon)
*/
class GClusterAdminClient {
private final InternalClient internalClient;
private final ClusterAdminClient clusterAdminClient;
def GClusterAdminClient(internalClient) {
this.internalClient = internalClient;
this.clusterAdminClient = internalClient.admin().cluster();
}
}

View File

@ -0,0 +1,40 @@
package org.elasticsearch.groovy.client
import org.elasticsearch.action.ActionListener
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse
import org.elasticsearch.client.IndicesAdminClient
import org.elasticsearch.client.internal.InternalClient
import org.elasticsearch.groovy.client.action.GActionFuture
/**
* @author kimchy (shay.banon)
*/
class GIndicesAdminClient {
private final InternalClient internalClient;
private final IndicesAdminClient indicesAdminClient;
def GIndicesAdminClient(internalClient) {
this.internalClient = internalClient;
this.indicesAdminClient = internalClient.admin().indices();
}
GActionFuture<RefreshResponse> refresh(Closure c) {
RefreshRequest request = new RefreshRequest()
c.setDelegate request
c.call()
refresh(request)
}
GActionFuture<RefreshResponse> refresh(RefreshRequest request) {
GActionFuture<RefreshResponse> future = new GActionFuture<RefreshResponse>(internalClient.threadPool(), request);
indicesAdminClient.refresh(request, future)
return future
}
void refresh(RefreshRequest request, ActionListener<RefreshResponse> listener) {
indicesAdminClient.refresh(request, listener)
}
}

View File

@ -0,0 +1,96 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.groovy.client.action;
import groovy.lang.Closure;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.support.PlainListenableActionFuture;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.util.TimeValue;
import java.util.concurrent.TimeUnit;
/**
* @author kimchy (shay.banon)
*/
public class GActionFuture<T> extends PlainListenableActionFuture<T> {
protected GActionFuture(ThreadPool threadPool, ActionRequest request) {
super(request.listenerThreaded(), threadPool);
}
public void setListener(final Closure listener) {
addListener(new ActionListener<T>() {
@Override public void onResponse(T t) {
listener.call(this);
}
@Override public void onFailure(Throwable e) {
listener.call(this);
}
});
}
public void setSuccess(final Closure success) {
addListener(new ActionListener<T>() {
@Override public void onResponse(T t) {
success.call(t);
}
@Override public void onFailure(Throwable e) {
// ignore
}
});
}
public void setFailure(final Closure failure) {
addListener(new ActionListener<T>() {
@Override public void onResponse(T t) {
// nothing
}
@Override public void onFailure(Throwable e) {
failure.call(e);
}
});
}
public T getResponse() {
return actionGet();
}
public T response(String timeout) throws ElasticSearchException {
return super.actionGet(timeout);
}
public T response(long timeoutMillis) throws ElasticSearchException {
return super.actionGet(timeoutMillis);
}
public T response(TimeValue timeout) throws ElasticSearchException {
return super.actionGet(timeout);
}
public T response(long timeout, TimeUnit unit) throws ElasticSearchException {
return super.actionGet(timeout, unit);
}
}

View File

@ -0,0 +1,50 @@
package org.elasticsearch.groovy.node
import org.elasticsearch.groovy.client.GClient
import org.elasticsearch.node.Node
/**
* @author kimchy (shay.banon)
*/
class GNode {
final Node node;
final GClient client;
def GNode(Node node) {
this.node = node;
this.client = new GClient(node.client())
}
/**
* The settings that were used to create the node.
*/
def getSettings() {
node.settings();
}
/**
* Start the node. If the node is already started, this method is no-op.
*/
def getStart() {
node.start()
this
}
/**
* Stops the node. If the node is already started, this method is no-op.
*/
def getStop() {
node.stop()
this
}
/**
* Closes the node (and {@link #stop} s if its running).
*/
def getClose() {
node.close()
this
}
}

View File

@ -0,0 +1,35 @@
package org.elasticsearch.groovy.node
import org.elasticsearch.groovy.util.json.JsonBuilder
import org.elasticsearch.node.Node
import org.elasticsearch.node.internal.InternalNode
import org.elasticsearch.util.settings.ImmutableSettings
import org.elasticsearch.util.settings.loader.JsonSettingsLoader
/**
* @author kimchy (shay.banon)
*/
public class GNodeBuilder {
private final ImmutableSettings.Builder settingsBuilder = ImmutableSettings.settingsBuilder();
private boolean loadConfigSettings = true;
public static GNodeBuilder nodeBuilder() {
new GNodeBuilder()
}
def settings(Closure settings) {
byte[] settingsBytes = new JsonBuilder().buildAsBytes(settings);
settingsBuilder.put(new JsonSettingsLoader().load(settingsBytes))
}
def getBuild() {
Node node = new InternalNode(settingsBuilder.build(), loadConfigSettings)
new GNode(node)
}
def getNode() {
build.start
}
}

View File

@ -0,0 +1,176 @@
package org.elasticsearch.groovy.util.json
import org.elasticsearch.ElasticSearchGenerationException
import org.elasticsearch.util.io.FastByteArrayOutputStream
import org.elasticsearch.util.io.FastCharArrayWriter
import static org.elasticsearch.util.json.Jackson.*
/**
* Used to build JSON data.
*
* @author Marc Palmer
* @author Graeme Rocher
*
* @since 1.2
*/
class JsonBuilder {
static NODE_ELEMENT = "element"
def root
def current
def nestingStack = []
def build(Closure c) {
return buildRoot(c)
}
String buildAsString(Closure c) {
FastCharArrayWriter writer = FastCharArrayWriter.Cached.cached();
try {
def json = build(c)
defaultObjectMapper().writeValue(writer, json);
} catch (IOException e) {
throw new ElasticSearchGenerationException("Failed to generate [" + c + "]", e);
}
return writer.toStringTrim()
}
byte[] buildAsBytes(Closure c) {
FastByteArrayOutputStream os = FastByteArrayOutputStream.Cached.cached();
try {
def json = build(c)
defaultObjectMapper().writeValue(os, json);
} catch (IOException e) {
throw new ElasticSearchGenerationException("Failed to generate [" + c + "]", e);
}
return os.copiedByteArray()
}
private buildRoot(Closure c) {
c.delegate = this
//c.resolveStrategy = Closure.DELEGATE_FIRST
root = [:]
current = root
def returnValue = c.call()
if (!root) {
return returnValue
}
return root
}
def invokeMethod(String methodName) {
current[methodName] = []
}
List array(Closure c) {
def prev = current
def list = []
try {
current = list
c.call(list)
}
finally {
current = prev
}
return list
}
def invokeMethod(String methodName, Object args) {
if (args.size()) {
if (args[0] instanceof Map) {
// switch root to an array if elements used at top level
if ((current == root) && (methodName == NODE_ELEMENT) && !(root instanceof List)) {
if (root.size()) {
throw new IllegalArgumentException('Cannot have array elements in root node if properties of root have already been set')
} else {
root = []
current = root
}
}
def n = [:]
if (current instanceof List) {
current << n
} else {
current[methodName] = n
}
n.putAll(args[0])
} else if (args[-1] instanceof Closure) {
final Object callable = args[-1]
handleClosureNode(methodName, callable)
} else if (args.size() == 1) {
if (methodName != NODE_ELEMENT) {
throw new IllegalArgumentException('Array elements must be defined with the "element" method call eg: element(value)')
}
// switch root to an array if elements used at top level
if (current == root) {
if (root.size() && methodName != NODE_ELEMENT) {
throw new IllegalArgumentException('Cannot have array elements in root node if properties of root have already been set')
} else if (!(root instanceof List)) {
root = []
current = root
}
}
if (current instanceof List) {
current << args[0]
} else {
throw new IllegalArgumentException('Array elements can only be defined under "array" nodes')
}
} else {
throw new IllegalArgumentException("This builder does not support invocation of [$methodName] with arg list ${args.dump()}")
}
} else {
current[methodName] = []
}
}
private handleClosureNode(String methodName, callable) {
def n = [:]
nestingStack << current
if (current instanceof List) {
current << n
}
else {
current[methodName] = n
}
current = n
callable.call()
current = nestingStack.pop()
}
void setProperty(String propName, Object value) {
if (value instanceof Closure) {
handleClosureNode(propName, value)
}
else if (value instanceof List) {
value = value.collect {
if (it instanceof Closure) {
def callable = it
final JsonBuilder localBuilder = new JsonBuilder()
callable.delegate = localBuilder
callable.resolveStrategy = Closure.DELEGATE_FIRST
final Map nestedObject = localBuilder.buildRoot(callable)
return nestedObject
}
else {
return it
}
}
current[propName] = value
}
else {
current[propName] = value
}
}
def getProperty(String propName) {
current[propName]
}
}

View File

@ -0,0 +1,21 @@
log4j.rootLogger=INFO, out
log4j.logger.jgroups=WARN
#log4j.logger.discovery=TRACE
#log4j.logger.cluster.service=TRACE
#log4j.logger.cluster.action.shard=DEBUG
#log4j.logger.indices.cluster=DEBUG
#log4j.logger.index=TRACE
#log4j.logger.index.engine=DEBUG
#log4j.logger.index.shard.service=DEBUG
#log4j.logger.index.shard.recovery=DEBUG
#log4j.logger.index.cache=DEBUG
#log4j.logger.http=TRACE
#log4j.logger.monitor.memory=TRACE
#log4j.logger.monitor.memory=TRACE
#log4j.logger.cluster.action.shard=TRACE
#log4j.logger.index.gateway=TRACE
log4j.appender.out=org.apache.log4j.ConsoleAppender
log4j.appender.out.layout=org.apache.log4j.PatternLayout
log4j.appender.out.layout.ConversionPattern=[%d{ABSOLUTE}][%-5p][%-25c] %m%n

View File

@ -0,0 +1,136 @@
package org.elasticsearch.groovy.test.client
import java.util.concurrent.CountDownLatch
import org.elasticsearch.action.index.IndexRequest
import org.elasticsearch.action.index.IndexResponse
import org.elasticsearch.groovy.node.GNode
import org.elasticsearch.groovy.node.GNodeBuilder
import static org.elasticsearch.client.Requests.*
/**
* @author kimchy (shay.banon)
*/
class SimpleActionsTests extends GroovyTestCase {
void testSimpleOperations() {
GNodeBuilder nodeBuilder = new GNodeBuilder()
nodeBuilder.settings {
node {
local = true
}
}
GNode node = nodeBuilder.node
def response = node.client.index(new IndexRequest(
index: "test",
type: "type1",
id: "1",
source: {
test = "value"
complex {
value1 = "value1"
value2 = "value2"
}
})).response
assertEquals "test", response.index
assertEquals "type1", response.type
assertEquals "1", response.id
def refresh = node.client.admin.indices.refresh {}
assertEquals 0, refresh.response.failedShards
def getR = node.client.get {
index "test"
type "type1"
id "1"
}
assertTrue getR.response.exists
assertEquals "test", getR.response.index
assertEquals "type1", getR.response.type
assertEquals "1", getR.response.id
assertEquals '{"test":"value","complex":{"value1":"value1","value2":"value2"}}', getR.response.sourceAsString()
assertEquals "value", getR.response.source.test
assertEquals "value1", getR.response.source.complex.value1
response = node.client.index({
index = "test"
type = "type1"
id = "1"
source = {
test = "value"
complex {
value1 = "value1"
value2 = "value2"
}
}
}).response
assertEquals "test", response.index
assertEquals "type1", response.type
assertEquals "1", response.id
def indexR = node.client.index(indexRequest().with {
index "test"
type "type1"
id "1"
source {
test = "value"
complex {
value1 = "value1"
value2 = "value2"
}
}
})
CountDownLatch latch = new CountDownLatch(1)
indexR.success = {IndexResponse responseX ->
assertEquals "test", responseX.index
assertEquals "test", indexR.response.index
assertEquals "type1", responseX.type
assertEquals "type1", indexR.response.type
assertEquals "1", responseX.id
assertEquals "1", indexR.response.id
latch.countDown()
}
latch.await()
indexR = node.client.index {
index "test"
type "type1"
id "1"
source {
test = "value"
complex {
value1 = "value1"
value2 = "value2"
}
}
}
latch = new CountDownLatch(1)
indexR.listener = {
assertEquals "test", indexR.response.index
assertEquals "type1", indexR.response.type
assertEquals "1", indexR.response.id
latch.countDown()
}
latch.await()
def delete = node.client.delete {
index "test"
type "type1"
id "1"
}
assertEquals "test", delete.response.index
assertEquals "type1", delete.response.type
assertEquals "1", delete.response.id
refresh = node.client.admin.indices.refresh {}
assertEquals 0, refresh.response.failedShards
getR = node.client.get {
index "test"
type "type1"
id "1"
}
assertFalse getR.response.exists
}
}

View File

@ -0,0 +1,25 @@
package org.elasticsearch.groovy.test.node
import org.elasticsearch.groovy.node.GNode
import org.elasticsearch.groovy.node.GNodeBuilder
import static org.elasticsearch.groovy.node.GNodeBuilder.*
/**
* @author kimchy (shay.banon)
*/
class GNodeBuilderTests extends GroovyTestCase {
void testGNodeBuilder() {
GNodeBuilder nodeBuilder = nodeBuilder();
nodeBuilder.settings {
node {
local = true
}
cluster {
name = "test"
}
}
GNode node = nodeBuilder.node
node.stop.close
}
}

View File

@ -0,0 +1,148 @@
package org.elasticsearch.groovy.util.json
/**
* @author kimchy (shay.banon)
*/
class JsonBuilderTests extends GroovyTestCase {
void testSimple() {
def builder = new JsonBuilder()
def result = builder.buildAsString {
rootprop = "something"
}
assertEquals '{"rootprop":"something"}', result.toString()
}
void testArrays() {
def builder = new JsonBuilder()
def result = builder.buildAsString {
categories = ['a', 'b', 'c']
rootprop = "something"
}
assertEquals '{"categories":["a","b","c"],"rootprop":"something"}', result.toString()
}
void testSubObjects() {
def builder = new JsonBuilder()
def result = builder.buildAsString {
categories = ['a', 'b', 'c']
rootprop = "something"
test {
subprop = 10
}
}
assertEquals '{"categories":["a","b","c"],"rootprop":"something","test":{"subprop":10}}', result.toString()
}
void testAssignedObjects() {
def builder = new JsonBuilder()
def result = builder.buildAsString {
categories = ['a', 'b', 'c']
rootprop = "something"
test = {
subprop = 10
}
}
assertEquals '{"categories":["a","b","c"],"rootprop":"something","test":{"subprop":10}}', result.toString()
}
void testNamedArgumentHandling() {
def builder = new JsonBuilder()
def result = builder.buildAsString {
categories = ['a', 'b', 'c']
rootprop = "something"
test subprop: 10, three: [1, 2, 3]
}
assertEquals '{"categories":["a","b","c"],"rootprop":"something","test":{"subprop":10,"three":[1,2,3]}}', result.toString()
}
void testArrayOfClosures() {
def builder = new JsonBuilder()
def result = builder.buildAsString {
foo = [{ bar = "hello" }]
}
assertEquals '{"foo":[{"bar":"hello"}]}', result.toString()
}
void testRootElementList() {
def builder = new JsonBuilder()
def results = ['one', 'two', 'three']
def result = builder.buildAsString {
for (b in results) {
element b
}
}
assertEquals '["one","two","three"]', result.toString()
result = builder.buildAsString {
results
}
assertEquals '["one","two","three"]', result.toString()
}
void testExampleFromReferenceGuide() {
def builder = new JsonBuilder()
def results = ['one', 'two', 'three']
def result = builder.buildAsString {
for (b in results) {
element title: b
}
}
assertEquals '[{"title":"one"},{"title":"two"},{"title":"three"}]', result.toString()
result = builder.buildAsString {
books = results.collect {
[title: it]
}
}
assertEquals '{"books":[{"title":"one"},{"title":"two"},{"title":"three"}]}', result.toString()
result = builder.buildAsString {
books = array {
for (b in results) {
book title: b
}
}
}
assertEquals '{"books":[{"title":"one"},{"title":"two"},{"title":"three"}]}', result.toString()
}
void testAppendToArray() {
def builder = new JsonBuilder()
def results = ['one', 'two', 'three']
def result = builder.buildAsString {
books = array {list ->
for (b in results) {
list << [title: b]
}
}
}
assertEquals '{"books":[{"title":"one"},{"title":"two"},{"title":"three"}]}', result.toString()
}
}

View File

@ -7,6 +7,7 @@ include 'test-integration'
include 'benchmark-micro' include 'benchmark-micro'
include 'plugins-attachments' include 'plugins-attachments'
include 'plugins-groovy'
rootProject.name = 'elasticsearch-root' rootProject.name = 'elasticsearch-root'
rootProject.children.each {project -> rootProject.children.each {project ->