start work on the groovy plugin
This commit is contained in:
parent
913bc2f947
commit
cb7e92b0f8
|
@ -30,6 +30,7 @@
|
|||
<w>jgroups</w>
|
||||
<w>joda</w>
|
||||
<w>jsonp</w>
|
||||
<w>kimchy</w>
|
||||
<w>lifecycle</w>
|
||||
<w>linefeeds</w>
|
||||
<w>lucene</w>
|
||||
|
|
|
@ -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-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-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-testng.iml" filepath="$PROJECT_DIR$/.idea/modules//test-testng.iml" />
|
||||
</modules>
|
||||
|
|
|
@ -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>
|
||||
|
|
@ -18,6 +18,12 @@ explodedDistLibDir = new File(explodedDistDir, 'lib')
|
|||
explodedDistBinDir = new File(explodedDistDir, 'bin')
|
||||
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 {
|
||||
group = 'org.elasticsearch'
|
||||
|
|
|
@ -97,15 +97,12 @@ artifacts {
|
|||
|
||||
uploadArchives {
|
||||
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
|
||||
repository(url: "http://oss.sonatype.org/service/local/staging/deploy/maven2/") {
|
||||
authentication(userName: "kimchy", password: System.getenv("REPO_PASS"))
|
||||
repository(url: rootProject.mavenRepoUrl) {
|
||||
authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
|
||||
}
|
||||
snapshotRepository(url: "http://oss.sonatype.org/content/repositories/snapshots") {
|
||||
authentication(userName: "kimchy", password: System.getenv("REPO_PASS"))
|
||||
snapshotRepository(url: rootProject.mavenSnapshotRepoUrl) {
|
||||
authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
|
||||
}
|
||||
|
||||
pom.project {
|
||||
|
|
|
@ -20,11 +20,19 @@
|
|||
package org.elasticsearch.action;
|
||||
|
||||
/**
|
||||
* @author kimchy (Shay Banon)
|
||||
* A listener for action responses or failures.
|
||||
*
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public interface ActionListener<Response> {
|
||||
|
||||
/**
|
||||
* A response handler.
|
||||
*/
|
||||
void onResponse(Response response);
|
||||
|
||||
/**
|
||||
* A failure handler.
|
||||
*/
|
||||
void onFailure(Throwable e);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.action;
|
|||
import org.elasticsearch.util.io.stream.Streamable;
|
||||
|
||||
/**
|
||||
* @author kimchy (Shay Banon)
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public interface ActionRequest extends Streamable {
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.action;
|
|||
import org.elasticsearch.util.io.stream.Streamable;
|
||||
|
||||
/**
|
||||
* @author kimchy (Shay Banon)
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public interface ActionResponse extends Streamable {
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -58,6 +58,13 @@ public class DeleteResponse implements ActionResponse, Streamable {
|
|||
return this.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* The index the document was deleted from.
|
||||
*/
|
||||
public String getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the document deleted.
|
||||
*/
|
||||
|
@ -65,6 +72,13 @@ public class DeleteResponse implements ActionResponse, Streamable {
|
|||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the document deleted.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the document deleted.
|
||||
*/
|
||||
|
@ -72,6 +86,13 @@ public class DeleteResponse implements ActionResponse, Streamable {
|
|||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the document deleted.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override public void readFrom(StreamInput in) throws IOException {
|
||||
index = in.readUTF();
|
||||
id = in.readUTF();
|
||||
|
|
|
@ -50,10 +50,18 @@ public class GetField implements Streamable, Iterable<Object> {
|
|||
return name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<Object> values() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public List<Object> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override public Iterator<Object> iterator() {
|
||||
return values.iterator();
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
|
|||
|
||||
private Map<String, GetField> fields;
|
||||
|
||||
private Map<String, Object> sourceAsMap;
|
||||
|
||||
private byte[] source;
|
||||
|
||||
GetResponse() {
|
||||
|
@ -79,6 +81,13 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
|
|||
return exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the document exists.
|
||||
*/
|
||||
public boolean isExists() {
|
||||
return exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* The index the document was fetched from.
|
||||
*/
|
||||
|
@ -86,6 +95,13 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
|
|||
return this.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* The index the document was fetched from.
|
||||
*/
|
||||
public String getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the document.
|
||||
*/
|
||||
|
@ -93,6 +109,13 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
|
|||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the document.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the document.
|
||||
*/
|
||||
|
@ -100,6 +123,13 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
|
|||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the document.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source of the document if exists.
|
||||
*/
|
||||
|
@ -125,17 +155,29 @@ public class GetResponse implements ActionResponse, Streamable, Iterable<GetFiel
|
|||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (sourceAsMap != null) {
|
||||
return sourceAsMap;
|
||||
}
|
||||
try {
|
||||
return defaultObjectMapper().readValue(source, 0, source.length, Map.class);
|
||||
sourceAsMap = defaultObjectMapper().readValue(source, 0, source.length, Map.class);
|
||||
return sourceAsMap;
|
||||
} catch (Exception e) {
|
||||
throw new ElasticSearchParseException("Failed to parse source to map", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getSource() {
|
||||
return sourceAsMap();
|
||||
}
|
||||
|
||||
public Map<String, GetField> fields() {
|
||||
return this.fields;
|
||||
}
|
||||
|
||||
public Map<String, GetField> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public GetField field(String name) {
|
||||
return fields.get(name);
|
||||
}
|
||||
|
|
|
@ -104,6 +104,9 @@ public class IndexRequest extends ShardReplicationOperationRequest {
|
|||
private byte[] source;
|
||||
private OpType opType = OpType.INDEX;
|
||||
|
||||
public IndexRequest() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new index request against the specific index. The {@link #type(String)},
|
||||
* {@link #id(String)} and {@link #source(byte[])} must be set.
|
||||
|
@ -127,9 +130,6 @@ public class IndexRequest extends ShardReplicationOperationRequest {
|
|||
this.source = source;
|
||||
}
|
||||
|
||||
IndexRequest() {
|
||||
}
|
||||
|
||||
@Override public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = super.validate();
|
||||
if (type == null) {
|
||||
|
|
|
@ -58,6 +58,13 @@ public class IndexResponse implements ActionResponse, Streamable {
|
|||
return this.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* The index the document was indexed into.
|
||||
*/
|
||||
public String getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the document indexed.
|
||||
*/
|
||||
|
@ -65,6 +72,13 @@ public class IndexResponse implements ActionResponse, Streamable {
|
|||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the document indexed.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the document indexed.
|
||||
*/
|
||||
|
@ -72,6 +86,13 @@ public class IndexResponse implements ActionResponse, Streamable {
|
|||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the document indexed.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override public void readFrom(StreamInput in) throws IOException {
|
||||
index = in.readUTF();
|
||||
id = in.readUTF();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -19,143 +19,16 @@
|
|||
|
||||
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)
|
||||
*/
|
||||
public class PlainActionFuture<T> implements ActionFuture<T>, ActionListener<T> {
|
||||
public class PlainActionFuture<T> extends AdapterActionFuture<T, T> {
|
||||
|
||||
public static <T> PlainActionFuture<T> newFuture() {
|
||||
return new PlainActionFuture<T>();
|
||||
}
|
||||
|
||||
private final CountDownLatch latch;
|
||||
|
||||
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();
|
||||
@Override protected T convert(T listenerResponse) {
|
||||
return listenerResponse;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -66,6 +66,13 @@ public abstract class BroadcastOperationResponse implements ActionResponse {
|
|||
return totalShards;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total shards this request ran against.
|
||||
*/
|
||||
public int getTotalShards() {
|
||||
return totalShards;
|
||||
}
|
||||
|
||||
/**
|
||||
* The successful shards this request was executed on.
|
||||
*/
|
||||
|
@ -73,6 +80,13 @@ public abstract class BroadcastOperationResponse implements ActionResponse {
|
|||
return successfulShards;
|
||||
}
|
||||
|
||||
/**
|
||||
* The successful shards this request was executed on.
|
||||
*/
|
||||
public int getSuccessfulShards() {
|
||||
return successfulShards;
|
||||
}
|
||||
|
||||
/**
|
||||
* The failed shards this request was executed on.
|
||||
*/
|
||||
|
@ -80,6 +94,13 @@ public abstract class BroadcastOperationResponse implements ActionResponse {
|
|||
return failedShards;
|
||||
}
|
||||
|
||||
/**
|
||||
* The failed shards this request was executed on.
|
||||
*/
|
||||
public int getFailedShards() {
|
||||
return failedShards;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of shard failures exception.
|
||||
*/
|
||||
|
@ -90,6 +111,13 @@ public abstract class BroadcastOperationResponse implements ActionResponse {
|
|||
return shardFailures;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of shard failures exception.
|
||||
*/
|
||||
public List<ShardOperationFailedException> getShardFailures() {
|
||||
return shardFailures;
|
||||
}
|
||||
|
||||
@Override public void readFrom(StreamInput in) throws IOException {
|
||||
totalShards = in.readVInt();
|
||||
successfulShards = in.readVInt();
|
||||
|
|
|
@ -53,6 +53,10 @@ import org.elasticsearch.action.terms.TermsRequest;
|
|||
*/
|
||||
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
|
||||
* set as well and optionally the {@link IndexRequest#id(String)}.
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -44,14 +44,17 @@ import org.elasticsearch.action.terms.TermsRequest;
|
|||
import org.elasticsearch.action.terms.TermsResponse;
|
||||
import org.elasticsearch.action.terms.TransportTermsAction;
|
||||
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.settings.Settings;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
|
@ -73,12 +76,13 @@ public class NodeClient extends AbstractComponent implements Client {
|
|||
|
||||
private final TransportMoreLikeThisAction moreLikeThisAction;
|
||||
|
||||
@Inject public NodeClient(Settings settings, NodeAdminClient admin,
|
||||
@Inject public NodeClient(Settings settings, ThreadPool threadPool, NodeAdminClient admin,
|
||||
TransportIndexAction indexAction, TransportDeleteAction deleteAction,
|
||||
TransportDeleteByQueryAction deleteByQueryAction, TransportGetAction getAction, TransportCountAction countAction,
|
||||
TransportSearchAction searchAction, TransportSearchScrollAction searchScrollAction,
|
||||
TransportTermsAction termsAction, TransportMoreLikeThisAction moreLikeThisAction) {
|
||||
super(settings);
|
||||
this.threadPool = threadPool;
|
||||
this.admin = admin;
|
||||
this.indexAction = indexAction;
|
||||
this.deleteAction = deleteAction;
|
||||
|
@ -91,6 +95,10 @@ public class NodeClient extends AbstractComponent implements Client {
|
|||
this.moreLikeThisAction = moreLikeThisAction;
|
||||
}
|
||||
|
||||
@Override public ThreadPool threadPool() {
|
||||
return this.threadPool;
|
||||
}
|
||||
|
||||
@Override public void close() {
|
||||
// nothing really to do
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ import org.elasticsearch.action.search.SearchScrollRequest;
|
|||
import org.elasticsearch.action.terms.TermsRequest;
|
||||
import org.elasticsearch.action.terms.TermsResponse;
|
||||
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.support.InternalTransportClient;
|
||||
import org.elasticsearch.cluster.ClusterNameModule;
|
||||
|
@ -77,7 +77,7 @@ import static org.elasticsearch.util.settings.ImmutableSettings.*;
|
|||
*
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public class TransportClient implements Client {
|
||||
public class TransportClient implements InternalClient {
|
||||
|
||||
private final Injector injector;
|
||||
|
||||
|
@ -209,6 +209,10 @@ public class TransportClient implements Client {
|
|||
ThreadLocals.clearReferencesThreadLocals();
|
||||
}
|
||||
|
||||
@Override public ThreadPool threadPool() {
|
||||
return internalClient.threadPool();
|
||||
}
|
||||
|
||||
@Override public AdminClient admin() {
|
||||
return internalClient.admin();
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.elasticsearch.action.search.SearchScrollRequest;
|
|||
import org.elasticsearch.action.terms.TermsRequest;
|
||||
import org.elasticsearch.action.terms.TermsResponse;
|
||||
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.action.count.ClientTransportCountAction;
|
||||
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.terms.ClientTransportTermsAction;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.util.component.AbstractComponent;
|
||||
import org.elasticsearch.util.settings.Settings;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
|
@ -82,12 +85,14 @@ public class InternalTransportClient extends AbstractComponent implements Client
|
|||
|
||||
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,
|
||||
ClientTransportDeleteByQueryAction deleteByQueryAction, ClientTransportCountAction countAction,
|
||||
ClientTransportSearchAction searchAction, ClientTransportSearchScrollAction searchScrollAction,
|
||||
ClientTransportTermsAction termsAction, ClientTransportMoreLikeThisAction moreLikeThisAction) {
|
||||
super(settings);
|
||||
this.threadPool = threadPool;
|
||||
this.nodesService = nodesService;
|
||||
this.adminClient = adminClient;
|
||||
|
||||
|
@ -106,6 +111,10 @@ public class InternalTransportClient extends AbstractComponent implements Client
|
|||
// nothing to do here
|
||||
}
|
||||
|
||||
@Override public ThreadPool threadPool() {
|
||||
return this.threadPool;
|
||||
}
|
||||
|
||||
@Override public AdminClient admin() {
|
||||
return adminClient;
|
||||
}
|
||||
|
|
|
@ -21,73 +21,22 @@ package org.elasticsearch.transport;
|
|||
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
import org.elasticsearch.ElasticSearchInterruptedException;
|
||||
import org.elasticsearch.util.concurrent.AbstractFuture;
|
||||
import org.elasticsearch.util.io.stream.Streamable;
|
||||
|
||||
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 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 volatile boolean done;
|
||||
private volatile boolean canceled;
|
||||
private volatile V result;
|
||||
private volatile Exception exp;
|
||||
|
||||
public PlainTransportFuture(TransportResponseHandler<V> 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 {
|
||||
|
@ -123,25 +72,13 @@ public class PlainTransportFuture<V extends Streamable> implements TransportFutu
|
|||
}
|
||||
|
||||
@Override public void handleResponse(V response) {
|
||||
this.done = true;
|
||||
this.result = response;
|
||||
|
||||
if (canceled)
|
||||
return;
|
||||
|
||||
handler.handleResponse(response);
|
||||
latch.countDown();
|
||||
set(response);
|
||||
}
|
||||
|
||||
@Override public void handleException(RemoteTransportException exp) {
|
||||
this.done = true;
|
||||
this.exp = exp;
|
||||
|
||||
if (canceled)
|
||||
return;
|
||||
|
||||
handler.handleException(exp);
|
||||
latch.countDown();
|
||||
setException(exp);
|
||||
}
|
||||
|
||||
@Override public boolean spawn() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -65,8 +65,6 @@ public class BinaryJsonBuilder extends JsonBuilder<BinaryJsonBuilder> {
|
|||
|
||||
private final JsonFactory factory;
|
||||
|
||||
private StringBuilder cachedStringBuilder;
|
||||
|
||||
public BinaryJsonBuilder() throws IOException {
|
||||
this(Jackson.defaultJsonFactory());
|
||||
}
|
||||
|
@ -85,10 +83,6 @@ public class BinaryJsonBuilder extends JsonBuilder<BinaryJsonBuilder> {
|
|||
this.builder = this;
|
||||
}
|
||||
|
||||
@Override protected StringBuilder cachedStringBuilder() {
|
||||
return cachedStringBuilder;
|
||||
}
|
||||
|
||||
@Override public BinaryJsonBuilder raw(byte[] json) throws IOException {
|
||||
flush();
|
||||
bos.write(json);
|
||||
|
|
|
@ -69,6 +69,8 @@ public abstract class JsonBuilder<T extends JsonBuilder> {
|
|||
|
||||
protected FieldCaseConversion fieldCaseConversion = globalFieldCaseConversion;
|
||||
|
||||
protected StringBuilder cachedStringBuilder;
|
||||
|
||||
public static StringJsonBuilder stringJsonBuilder() throws IOException {
|
||||
return StringJsonBuilder.Cached.cached();
|
||||
}
|
||||
|
@ -143,9 +145,9 @@ public abstract class JsonBuilder<T extends JsonBuilder> {
|
|||
|
||||
public T field(String name) throws IOException {
|
||||
if (fieldCaseConversion == FieldCaseConversion.UNDERSCORE) {
|
||||
name = Strings.toUnderscoreCase(name);
|
||||
name = Strings.toUnderscoreCase(name, cachedStringBuilder);
|
||||
} else if (fieldCaseConversion == FieldCaseConversion.CAMELCASE) {
|
||||
name = Strings.toCamelCase(name);
|
||||
name = Strings.toCamelCase(name, cachedStringBuilder);
|
||||
}
|
||||
generator.writeFieldName(name);
|
||||
return builder;
|
||||
|
@ -403,10 +405,6 @@ public abstract class JsonBuilder<T extends JsonBuilder> {
|
|||
|
||||
public abstract String string() throws IOException;
|
||||
|
||||
protected StringBuilder cachedStringBuilder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
generator.close();
|
||||
|
|
|
@ -69,8 +69,6 @@ public class StringJsonBuilder extends JsonBuilder<StringJsonBuilder> {
|
|||
|
||||
final UnicodeUtil.UTF8Result utf8Result = new UnicodeUtil.UTF8Result();
|
||||
|
||||
private StringBuilder cachedStringBuilder;
|
||||
|
||||
public StringJsonBuilder() throws IOException {
|
||||
this(Jackson.defaultJsonFactory());
|
||||
}
|
||||
|
@ -89,10 +87,6 @@ public class StringJsonBuilder extends JsonBuilder<StringJsonBuilder> {
|
|||
this.builder = this;
|
||||
}
|
||||
|
||||
@Override protected StringBuilder cachedStringBuilder() {
|
||||
return cachedStringBuilder;
|
||||
}
|
||||
|
||||
@Override public StringJsonBuilder raw(byte[] json) throws IOException {
|
||||
flush();
|
||||
Unicode.UTF16Result result = Unicode.unsafeFromBytesAsUtf16(json);
|
||||
|
|
|
@ -107,15 +107,12 @@ artifacts {
|
|||
|
||||
uploadArchives {
|
||||
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
|
||||
repository(url: "http://oss.sonatype.org/service/local/staging/deploy/maven2/") {
|
||||
authentication(userName: "kimchy", password: System.getenv("REPO_PASS"))
|
||||
repository(url: rootProject.mavenRepoUrl) {
|
||||
authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
|
||||
}
|
||||
snapshotRepository(url: "http://oss.sonatype.org/content/repositories/snapshots") {
|
||||
authentication(userName: "kimchy", password: System.getenv("REPO_PASS"))
|
||||
snapshotRepository(url: rootProject.mavenSnapshotRepoUrl) {
|
||||
authentication(userName: rootProject.mavenRepoUser, password: rootProject.mavenRepoPass)
|
||||
}
|
||||
|
||||
pom.project {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ include 'test-integration'
|
|||
include 'benchmark-micro'
|
||||
|
||||
include 'plugins-attachments'
|
||||
include 'plugins-groovy'
|
||||
|
||||
rootProject.name = 'elasticsearch-root'
|
||||
rootProject.children.each {project ->
|
||||
|
|
Loading…
Reference in New Issue