plugins support + start work on attachments plugin

This commit is contained in:
kimchy 2010-03-27 23:43:59 +03:00
parent 73daff1584
commit b799b7a9d7
34 changed files with 871 additions and 27 deletions

View File

@ -40,6 +40,7 @@
<w>ngram</w>
<w>param</w>
<w>params</w>
<w>plugins</w>
<w>porterstem</w>
<w>rebalance</w>
<w>searchable</w>

9
.idea/libraries/tika.xml Normal file
View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="tika">
<CLASSES>
<root url="jar://$GRADLE_REPOSITORY$/org.apache.tika/tika-app/bundles/tika-app-0.6.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -5,6 +5,7 @@
<module fileurl="file://$PROJECT_DIR$/.idea/modules//benchmark-micro.iml" filepath="$PROJECT_DIR$/.idea/modules//benchmark-micro.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//plugin-attachments.iml" filepath="$PROJECT_DIR$/.idea/modules//plugin-attachments.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>

View File

@ -13,6 +13,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="elasticsearch" />
<orderEntry type="module" module-name="plugin-attachments" />
<orderEntry type="module" module-name="test-integration" />
</component>
</module>

View File

@ -0,0 +1,18 @@
<?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/attachments/build/classes/main" />
<output-test url="file://$MODULE_DIR$/../../plugins/attachments/build/classes/test" />
<exclude-output />
<content url="file://$MODULE_DIR$/../../plugins/attachments">
<sourceFolder url="file://$MODULE_DIR$/../../plugins/attachments/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../plugins/attachments/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/../../plugins/attachments/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="elasticsearch" />
<orderEntry type="library" name="tika" level="project" />
</component>
</module>

View File

@ -16,7 +16,7 @@
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="elasticsearch" />
<module name="elasticsearch-root" />
<envs />
<RunnerSettings RunnerId="Debug">
<option name="DEBUG_PORT" value="61134" />

View File

@ -0,0 +1,28 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Bootstrap [No Plugins]" type="Application" factoryName="Application">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea">
<pattern>
<option name="PATTERN" value="org.elasticsearch.bootstrap.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<extension name="snapshooter" />
<option name="MAIN_CLASS_NAME" value="org.elasticsearch.bootstrap.Bootstrap" />
<option name="VM_PARAMETERS" value="-server -Xmx1g -Des-foreground=yes -Djava.net.preferIPv4Stack=true" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" value="" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<module name="elasticsearch" />
<envs />
<RunnerSettings RunnerId="Profile ">
<option name="myExternalizedOptions" value="&#10;snapshots-dir=&#10;additional-options2=onexit\=snapshot&#10;" />
</RunnerSettings>
<RunnerSettings RunnerId="Run" />
<ConfigurationWrapper RunnerId="Run" />
<method />
</configuration>
</component>

View File

@ -90,7 +90,8 @@ zip.doFirst {task ->
}
}
task release(dependsOn: [zip]) {
task release(dependsOn: [zip, ":plugins-attachments:release"]) << {
ant.delete(dir: explodedDistDir)
}
task wrapper(type: Wrapper) {

View File

@ -31,7 +31,9 @@ import static org.elasticsearch.util.Strings.*;
import static org.elasticsearch.util.settings.ImmutableSettings.Builder.*;
/**
* @author kimchy (Shay Banon)
* The environment of where things exists.
*
* @author kimchy (shay.banon)
*/
public class Environment {
@ -43,6 +45,8 @@ public class Environment {
private final File configFile;
private final File pluginsFile;
private final File logsFile;
public Environment() {
@ -63,6 +67,12 @@ public class Environment {
configFile = new File(homeFile, "config");
}
if (settings.get("path.plugins") != null) {
pluginsFile = new File(cleanPath(settings.get("path.plugins")));
} else {
pluginsFile = new File(homeFile, "plugins");
}
if (settings.get("path.work") != null) {
workFile = new File(cleanPath(settings.get("path.work")));
} else {
@ -79,18 +89,31 @@ public class Environment {
}
}
/**
* The home of the installation.
*/
public File homeFile() {
return homeFile;
}
/**
* The work location.
*/
public File workFile() {
return workFile;
}
/**
* The config location.
*/
public File configFile() {
return configFile;
}
public File pluginsFile() {
return pluginsFile;
}
public File workWithClusterFile() {
return workWithClusterFile;
}

View File

@ -25,13 +25,14 @@ import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.jmx.JmxService;
import org.elasticsearch.jmx.MBean;
import org.elasticsearch.jmx.ManagedAttribute;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.settings.Settings;
/**
* @author kimchy (Shay Banon)
*/
@MBean(objectName = "", description = "")
public class IndexServiceManagement extends AbstractIndexComponent {
public class IndexServiceManagement extends AbstractIndexComponent implements CloseableComponent {
public static String buildIndexGroupName(Index index) {
return "service=indices,index=" + index.name();

View File

@ -27,6 +27,7 @@ import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexLifecycle;
import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.util.Nullable;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.settings.ImmutableSettings;
import org.elasticsearch.util.settings.Settings;
@ -38,7 +39,7 @@ import static com.google.common.collect.Maps.*;
* @author kimchy (Shay Banon)
*/
@IndexLifecycle
public class AnalysisService extends AbstractIndexComponent {
public class AnalysisService extends AbstractIndexComponent implements CloseableComponent {
private final ImmutableMap<String, AnalyzerProvider> analyzerProviders;

View File

@ -21,13 +21,12 @@ package org.elasticsearch.index.cache.filter;
import org.apache.lucene.search.Filter;
import org.elasticsearch.index.IndexComponent;
import org.elasticsearch.util.component.CloseableComponent;
/**
* @author kimchy (Shay Banon)
*/
public interface FilterCache extends IndexComponent {
public interface FilterCache extends IndexComponent, CloseableComponent {
Filter cache(Filter filterToCache);
void close();
}

View File

@ -32,6 +32,7 @@ import org.elasticsearch.index.shard.IndexShardLifecycle;
import org.elasticsearch.index.translog.Translog;
import org.elasticsearch.util.Nullable;
import org.elasticsearch.util.SizeValue;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.concurrent.ThreadSafe;
import org.elasticsearch.util.lease.Releasable;
@ -40,7 +41,7 @@ import org.elasticsearch.util.lease.Releasable;
*/
@ThreadSafe
@IndexShardLifecycle
public interface Engine extends IndexShardComponent {
public interface Engine extends IndexShardComponent, CloseableComponent {
/**
* Starts the Engine.
@ -83,8 +84,6 @@ public interface Engine extends IndexShardComponent {
*/
SizeValue estimateFlushableMemorySize();
void close() throws ElasticSearchException;
/**
* Recovery allow to start the recovery process. It is built of three phases.
*

View File

@ -20,11 +20,12 @@
package org.elasticsearch.index.gateway;
import org.elasticsearch.index.IndexComponent;
import org.elasticsearch.util.component.CloseableComponent;
/**
* @author kimchy (Shay Banon)
*/
public interface IndexGateway extends IndexComponent {
public interface IndexGateway extends IndexComponent, CloseableComponent {
Class<? extends IndexShardGateway> shardGatewayClass();
@ -32,9 +33,4 @@ public interface IndexGateway extends IndexComponent {
* Deletes the content of the index gateway.
*/
void delete();
/**
* Closes the index gateway.
*/
void close();
}

View File

@ -36,6 +36,7 @@ import org.elasticsearch.index.translog.Translog;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.util.StopWatch;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.settings.Settings;
import java.io.IOException;
@ -45,7 +46,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author kimchy (Shay Banon)
*/
public class IndexShardGatewayService extends AbstractIndexShardComponent {
public class IndexShardGatewayService extends AbstractIndexShardComponent implements CloseableComponent {
private final boolean snapshotOnClose;

View File

@ -47,6 +47,9 @@ import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.index.store.Store;
import org.elasticsearch.index.store.StoreModule;
import org.elasticsearch.index.translog.TranslogModule;
import org.elasticsearch.plugins.PluginsService;
import org.elasticsearch.plugins.ShardsPluginsModule;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.guice.Injectors;
import org.elasticsearch.util.settings.Settings;
@ -68,6 +71,8 @@ public class InternalIndexService extends AbstractIndexComponent implements Inde
private final Settings indexSettings;
private final PluginsService pluginsService;
private final MapperService mapperService;
private final IndexQueryParserService queryParserService;
@ -93,6 +98,8 @@ public class InternalIndexService extends AbstractIndexComponent implements Inde
this.similarityService = similarityService;
this.filterCache = filterCache;
this.operationRouting = operationRouting;
this.pluginsService = injector.getInstance(PluginsService.class);
}
@Override public int numberOfShards() {
@ -174,6 +181,7 @@ public class InternalIndexService extends AbstractIndexComponent implements Inde
logger.debug("Creating Shard Id [{}]", shardId.id());
Injector shardInjector = injector.createChildInjector(
new ShardsPluginsModule(indexSettings, pluginsService),
new IndexShardModule(shardId),
new StoreModule(indexSettings),
new DeletionPolicyModule(indexSettings),
@ -222,6 +230,11 @@ public class InternalIndexService extends AbstractIndexComponent implements Inde
IndexShard indexShard = tmpShardsMap.remove(shardId);
shards = ImmutableMap.copyOf(tmpShardsMap);
for (Class<? extends CloseableComponent> closeable : pluginsService.shardServices()) {
shardInjector.getInstance(closeable).close();
}
// close shard actions
shardInjector.getInstance(IndexShardManagement.class).close();

View File

@ -28,6 +28,7 @@ import org.elasticsearch.index.translog.Translog;
import org.elasticsearch.jmx.JmxService;
import org.elasticsearch.jmx.MBean;
import org.elasticsearch.jmx.ManagedAttribute;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.settings.Settings;
import java.io.IOException;
@ -38,7 +39,7 @@ import static org.elasticsearch.index.IndexServiceManagement.*;
* @author kimchy (Shay Banon)
*/
@MBean(objectName = "", description = "")
public class IndexShardManagement extends AbstractIndexShardComponent {
public class IndexShardManagement extends AbstractIndexShardComponent implements CloseableComponent {
public static String buildShardGroupName(ShardId shardId) {
return buildIndexGroupName(shardId.index()) + ",subService=shards,shard=" + shardId.id();

View File

@ -42,6 +42,7 @@ import org.elasticsearch.util.SizeUnit;
import org.elasticsearch.util.SizeValue;
import org.elasticsearch.util.StopWatch;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.io.VoidStreamable;
import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput;
@ -63,7 +64,7 @@ import static org.elasticsearch.util.concurrent.ConcurrentMaps.*;
/**
* @author kimchy (shay.banon)
*/
public class RecoveryAction extends AbstractIndexShardComponent {
public class RecoveryAction extends AbstractIndexShardComponent implements CloseableComponent {
private final SizeValue fileChunkSize;

View File

@ -30,6 +30,7 @@ import org.elasticsearch.index.shard.IndexShardLifecycle;
import org.elasticsearch.index.shard.IndexShardState;
import org.elasticsearch.util.Nullable;
import org.elasticsearch.util.SizeValue;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.concurrent.ThreadSafe;
/**
@ -37,7 +38,7 @@ import org.elasticsearch.util.concurrent.ThreadSafe;
*/
@IndexShardLifecycle
@ThreadSafe
public interface IndexShard extends IndexShardComponent {
public interface IndexShard extends IndexShardComponent, CloseableComponent {
ShardRouting routingEntry();
@ -74,8 +75,6 @@ public interface IndexShard extends IndexShardComponent {
Engine.Searcher searcher();
void close();
/**
* Returns <tt>true</tt> if this shard can ignore a recovery attempt made to it (since the already doing/done it)
*/

View File

@ -41,7 +41,10 @@ import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.settings.IndexSettingsModule;
import org.elasticsearch.index.similarity.SimilarityModule;
import org.elasticsearch.indices.cluster.IndicesClusterStateService;
import org.elasticsearch.plugins.IndicesPluginsModule;
import org.elasticsearch.plugins.PluginsService;
import org.elasticsearch.util.component.AbstractLifecycleComponent;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.concurrent.ThreadSafe;
import org.elasticsearch.util.guice.Injectors;
import org.elasticsearch.util.settings.Settings;
@ -66,6 +69,8 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
private final Injector injector;
private final PluginsService pluginsService;
private final Map<String, Injector> indicesInjectors = new HashMap<String, Injector>();
private volatile ImmutableMap<String, IndexService> indices = ImmutableMap.of();
@ -74,6 +79,8 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
super(settings);
this.clusterStateService = clusterStateService;
this.injector = injector;
this.pluginsService = injector.getInstance(PluginsService.class);
}
@Override protected void doStart() throws ElasticSearchException {
@ -155,6 +162,7 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
new IndexNameModule(index),
new LocalNodeIdModule(localNodeId),
new IndexSettingsModule(indexSettings),
new IndicesPluginsModule(indexSettings, pluginsService),
new AnalysisModule(indexSettings),
new SimilarityModule(indexSettings),
new FilterCacheModule(indexSettings),
@ -193,6 +201,10 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
IndexService indexService = tmpMap.remove(index);
indices = ImmutableMap.copyOf(tmpMap);
for (Class<? extends CloseableComponent> closeable : pluginsService.indexServices()) {
indexInjector.getInstance(closeable).close();
}
indexService.close();
indexInjector.getInstance(FilterCache.class).close();

View File

@ -0,0 +1,57 @@
/*
* 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.plugins;
import com.google.common.collect.ImmutableList;
import com.google.inject.Module;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.component.LifecycleComponent;
import java.util.Collection;
/**
* @author kimchy (shay.banon)
*/
public abstract class AbstractPlugin implements Plugin {
@Override public Collection<Class<? extends Module>> modules() {
return ImmutableList.of();
}
@Override public Collection<Class<? extends LifecycleComponent>> services() {
return ImmutableList.of();
}
@Override public Collection<Class<? extends Module>> indexModules() {
return ImmutableList.of();
}
@Override public Collection<Class<? extends CloseableComponent>> indexServices() {
return ImmutableList.of();
}
@Override public Collection<Class<? extends Module>> shardModules() {
return ImmutableList.of();
}
@Override public Collection<Class<? extends CloseableComponent>> shardServices() {
return ImmutableList.of();
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.plugins;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import org.elasticsearch.util.settings.Settings;
import java.util.Collection;
import static org.elasticsearch.util.guice.ModulesFactory.*;
/**
* @author kimchy (shay.banon)
*/
public class IndicesPluginsModule extends AbstractModule {
private final Settings settings;
private final PluginsService pluginsService;
public IndicesPluginsModule(Settings settings, PluginsService pluginsService) {
this.settings = settings;
this.pluginsService = pluginsService;
}
@Override protected void configure() {
Collection<Class<? extends Module>> modules = pluginsService.indexModules();
for (Class<? extends Module> module : modules) {
createModule(module, settings).configure(binder());
}
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.plugins;
import com.google.inject.Module;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.component.LifecycleComponent;
import java.util.Collection;
/**
* @author kimchy (shay.banon)
*/
public interface Plugin {
String name();
Collection<Class<? extends Module>> modules();
Collection<Class<? extends LifecycleComponent>> services();
Collection<Class<? extends Module>> indexModules();
Collection<Class<? extends CloseableComponent>> indexServices();
Collection<Class<? extends Module>> shardModules();
Collection<Class<? extends CloseableComponent>> shardServices();
}

View File

@ -0,0 +1,52 @@
/*
* 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.plugins;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import org.elasticsearch.util.settings.Settings;
import java.util.Collection;
import static org.elasticsearch.util.guice.ModulesFactory.*;
/**
* @author kimchy (shay.banon)
*/
public class PluginsModule extends AbstractModule {
private final Settings settings;
private final PluginsService pluginsService;
public PluginsModule(Settings settings, PluginsService pluginsService) {
this.settings = settings;
this.pluginsService = pluginsService;
}
@Override protected void configure() {
bind(PluginsService.class).toInstance(pluginsService);
Collection<Class<? extends Module>> modules = pluginsService.modules();
for (Class<? extends Module> module : modules) {
createModule(module, settings).configure(binder());
}
}
}

View File

@ -0,0 +1,278 @@
/*
* 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.plugins;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Module;
import org.elasticsearch.env.Environment;
import org.elasticsearch.util.component.AbstractComponent;
import org.elasticsearch.util.component.CloseableComponent;
import org.elasticsearch.util.component.LifecycleComponent;
import org.elasticsearch.util.io.Streams;
import org.elasticsearch.util.settings.Settings;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static com.google.common.collect.Maps.*;
import static org.elasticsearch.util.io.FileSystemUtils.*;
/**
* @author kimchy (shay.banon)
*/
public class PluginsService extends AbstractComponent {
private final Environment environment;
private final ImmutableMap<String, Plugin> plugins;
@Inject public PluginsService(Settings settings, Environment environment) {
super(settings);
this.environment = environment;
loadPluginsIntoClassLoader();
// first, find all the ones that are in the classpath
Map<String, Plugin> plugins = Maps.newHashMap();
plugins.putAll(loadPluginsFromClasspath(settings));
logger.info("Loaded {}", plugins.keySet());
this.plugins = ImmutableMap.copyOf(plugins);
}
public Settings updatedSettings() {
return this.settings;
}
public Collection<Class<? extends Module>> modules() {
List<Class<? extends Module>> modules = Lists.newArrayList();
for (Plugin plugin : plugins.values()) {
modules.addAll(plugin.modules());
}
return modules;
}
public Collection<Class<? extends LifecycleComponent>> services() {
List<Class<? extends LifecycleComponent>> services = Lists.newArrayList();
for (Plugin plugin : plugins.values()) {
services.addAll(plugin.services());
}
return services;
}
public Collection<Class<? extends Module>> indexModules() {
List<Class<? extends Module>> modules = Lists.newArrayList();
for (Plugin plugin : plugins.values()) {
modules.addAll(plugin.indexModules());
}
return modules;
}
public Collection<Class<? extends CloseableComponent>> indexServices() {
List<Class<? extends CloseableComponent>> services = Lists.newArrayList();
for (Plugin plugin : plugins.values()) {
services.addAll(plugin.indexServices());
}
return services;
}
public Collection<Class<? extends Module>> shardModules() {
List<Class<? extends Module>> modules = Lists.newArrayList();
for (Plugin plugin : plugins.values()) {
modules.addAll(plugin.shardModules());
}
return modules;
}
public Collection<Class<? extends CloseableComponent>> shardServices() {
List<Class<? extends CloseableComponent>> services = Lists.newArrayList();
for (Plugin plugin : plugins.values()) {
services.addAll(plugin.shardServices());
}
return services;
}
private void loadPluginsIntoClassLoader() {
File pluginsFile = environment.pluginsFile();
if (!pluginsFile.exists()) {
return;
}
if (!pluginsFile.isDirectory()) {
return;
}
ClassLoader classLoader = settings.getClassLoader();
Class classLoaderClass = classLoader.getClass();
Method addURL = null;
while (!classLoaderClass.equals(Object.class)) {
try {
addURL = classLoaderClass.getDeclaredMethod("addURL", URL.class);
addURL.setAccessible(true);
break;
} catch (NoSuchMethodException e) {
// no method, try the parent
classLoaderClass = classLoaderClass.getSuperclass();
}
}
if (addURL == null) {
logger.debug("Failed to find addURL method on classLoader [" + classLoader + "] to add methods");
return;
}
File[] pluginsFiles = pluginsFile.listFiles();
for (File pluginFile : pluginsFiles) {
if (!pluginFile.getName().endsWith(".zip")) {
continue;
}
if (logger.isTraceEnabled()) {
logger.trace("Processing [{}]", pluginFile);
}
String pluginNameNoExtension = pluginFile.getName().substring(0, pluginFile.getName().lastIndexOf('.'));
File extractedPluginDir = new File(new File(environment.workFile(), "plugins"), pluginNameNoExtension);
extractedPluginDir.mkdirs();
File stampsDir = new File(new File(environment.workFile(), "plugins"), "_stamps");
stampsDir.mkdirs();
boolean extractPlugin = true;
File stampFile = new File(stampsDir, pluginNameNoExtension + ".stamp");
if (stampFile.exists()) {
// read it, and check if its the same size as the pluginFile
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(stampFile, "r");
long size = raf.readLong();
if (size == pluginFile.length()) {
extractPlugin = false;
if (logger.isTraceEnabled()) {
logger.trace("--- No need to extract plugin, same size [" + size + "]");
}
}
} catch (Exception e) {
// ignore and extract the plugin
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
// ignore
}
}
}
}
if (extractPlugin) {
if (logger.isTraceEnabled()) {
logger.trace("--- Extracting plugin to [" + extractedPluginDir + "]");
}
deleteRecursively(extractedPluginDir, false);
ZipFile zipFile = null;
try {
zipFile = new ZipFile(pluginFile);
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry zipEntry = zipEntries.nextElement();
if (!(zipEntry.getName().endsWith(".jar") || zipEntry.getName().endsWith(".zip"))) {
continue;
}
String name = zipEntry.getName().replace('\\', '/');
File target = new File(extractedPluginDir, name);
Streams.copy(zipFile.getInputStream(zipEntry), new FileOutputStream(target));
}
} catch (Exception e) {
logger.warn("Failed to extract plugin [" + pluginFile + "], ignoring...", e);
continue;
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (IOException e) {
// ignore
}
}
}
try {
RandomAccessFile raf = new RandomAccessFile(stampFile, "rw");
raf.writeLong(pluginFile.length());
raf.close();
} catch (Exception e) {
// ignore
}
}
try {
for (File jarToAdd : extractedPluginDir.listFiles()) {
if (!(jarToAdd.getName().endsWith(".jar") || jarToAdd.getName().endsWith(".zip"))) {
continue;
}
addURL.invoke(classLoader, jarToAdd.toURI().toURL());
}
} catch (Exception e) {
logger.warn("Failed to add plugin [" + pluginFile + "]", e);
}
}
}
private Map<String, Plugin> loadPluginsFromClasspath(Settings settings) {
Map<String, Plugin> plugins = newHashMap();
Enumeration<URL> pluginUrls = null;
try {
pluginUrls = settings.getClassLoader().getResources("es-plugin.properties");
} catch (IOException e) {
logger.warn("Failed to find plugins from classpath", e);
}
while (pluginUrls.hasMoreElements()) {
URL pluginUrl = pluginUrls.nextElement();
Properties pluginProps = new Properties();
InputStream is = null;
try {
is = pluginUrl.openStream();
pluginProps.load(is);
String sPluginClass = pluginProps.getProperty("plugin");
Class<?> pluginClass = settings.getClassLoader().loadClass(sPluginClass);
Plugin plugin = (Plugin) pluginClass.newInstance();
plugins.put(plugin.name(), plugin);
} catch (Exception e) {
logger.warn("Failed to load plugin from [" + pluginUrl + "]", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// ignore
}
}
}
}
return plugins;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.plugins;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import org.elasticsearch.util.settings.Settings;
import java.util.Collection;
import static org.elasticsearch.util.guice.ModulesFactory.*;
/**
* @author kimchy (shay.banon)
*/
public class ShardsPluginsModule extends AbstractModule {
private final Settings settings;
private final PluginsService pluginsService;
public ShardsPluginsModule(Settings settings, PluginsService pluginsService) {
this.settings = settings;
this.pluginsService = pluginsService;
}
@Override protected void configure() {
Collection<Class<? extends Module>> modules = pluginsService.shardModules();
for (Class<? extends Module> module : modules) {
createModule(module, settings).configure(binder());
}
}
}

View File

@ -46,6 +46,8 @@ import org.elasticsearch.jmx.JmxModule;
import org.elasticsearch.jmx.JmxService;
import org.elasticsearch.monitor.MonitorModule;
import org.elasticsearch.monitor.MonitorService;
import org.elasticsearch.plugins.PluginsModule;
import org.elasticsearch.plugins.PluginsService;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestModule;
import org.elasticsearch.search.SearchModule;
@ -60,6 +62,7 @@ import org.elasticsearch.transport.TransportService;
import org.elasticsearch.util.ThreadLocals;
import org.elasticsearch.util.Tuple;
import org.elasticsearch.util.component.Lifecycle;
import org.elasticsearch.util.component.LifecycleComponent;
import org.elasticsearch.util.guice.Injectors;
import org.elasticsearch.util.io.FileSystemUtils;
import org.elasticsearch.util.logging.Loggers;
@ -86,6 +89,8 @@ public final class InternalServer implements Server {
private final Environment environment;
private final PluginsService pluginsService;
private final Client client;
public InternalServer() throws ElasticSearchException {
@ -94,13 +99,16 @@ public final class InternalServer implements Server {
public InternalServer(Settings pSettings, boolean loadConfigSettings) throws ElasticSearchException {
Tuple<Settings, Environment> tuple = InternalSettingsPerparer.prepareSettings(pSettings, loadConfigSettings);
this.settings = tuple.v1();
this.environment = tuple.v2();
Logger logger = Loggers.getLogger(Server.class, settings.get("name"));
Logger logger = Loggers.getLogger(Server.class, tuple.v1().get("name"));
logger.info("{{}}: Initializing ...", Version.full());
this.pluginsService = new PluginsService(tuple.v1(), tuple.v2());
this.settings = pluginsService.updatedSettings();
this.environment = tuple.v2();
ArrayList<Module> modules = new ArrayList<Module>();
modules.add(new PluginsModule(settings, pluginsService));
modules.add(new ServerModule(this));
modules.add(new JmxModule(settings));
modules.add(new EnvironmentModule(environment));
@ -146,6 +154,10 @@ public final class InternalServer implements Server {
Logger logger = Loggers.getLogger(Server.class, settings.get("name"));
logger.info("{{}}: Starting ...", Version.full());
for (Class<? extends LifecycleComponent> plugin : pluginsService.services()) {
injector.getInstance(plugin).start();
}
injector.getInstance(IndicesService.class).start();
injector.getInstance(GatewayService.class).start();
injector.getInstance(ClusterService.class).start();
@ -186,6 +198,10 @@ public final class InternalServer implements Server {
injector.getInstance(TransportService.class).stop();
injector.getInstance(JmxService.class).close();
for (Class<? extends LifecycleComponent> plugin : pluginsService.services()) {
injector.getInstance(plugin).stop();
}
// Not pretty, but here we go
try {
FileSystemUtils.deleteRecursively(new File(new File(environment.workWithClusterFile(), FsStores.DEFAULT_INDICES_LOCATION),
@ -226,6 +242,10 @@ public final class InternalServer implements Server {
injector.getInstance(RestController.class).close();
injector.getInstance(TransportService.class).close();
for (Class<? extends LifecycleComponent> plugin : pluginsService.services()) {
injector.getInstance(plugin).close();
}
injector.getInstance(TimerService.class).close();
injector.getInstance(ThreadPool.class).shutdown();
try {

View File

@ -22,6 +22,9 @@ package org.elasticsearch.util.component;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.util.settings.Settings;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author kimchy (shay.banon)
*/
@ -29,6 +32,8 @@ public abstract class AbstractLifecycleComponent<T> extends AbstractComponent im
protected final Lifecycle lifecycle = new Lifecycle();
private final List<LifecycleListener> listeners = new CopyOnWriteArrayList<LifecycleListener>();
protected AbstractLifecycleComponent(Settings settings) {
super(settings);
}
@ -45,11 +50,25 @@ public abstract class AbstractLifecycleComponent<T> extends AbstractComponent im
return this.lifecycle.state();
}
@Override public void addLifecycleListener(LifecycleListener listener) {
listeners.add(listener);
}
@Override public void removeLifecycleListener(LifecycleListener listener) {
listeners.remove(listener);
}
@SuppressWarnings({"unchecked"}) @Override public T start() throws ElasticSearchException {
if (!lifecycle.moveToStarted()) {
return (T) this;
}
for (LifecycleListener listener : listeners) {
listener.beforeStart();
}
doStart();
for (LifecycleListener listener : listeners) {
listener.afterStart();
}
return (T) this;
}
@ -59,7 +78,13 @@ public abstract class AbstractLifecycleComponent<T> extends AbstractComponent im
if (!lifecycle.moveToStopped()) {
return (T) this;
}
for (LifecycleListener listener : listeners) {
listener.beforeStop();
}
doStop();
for (LifecycleListener listener : listeners) {
listener.afterStop();
}
return (T) this;
}
@ -72,7 +97,13 @@ public abstract class AbstractLifecycleComponent<T> extends AbstractComponent im
if (!lifecycle.moveToClosed()) {
return;
}
for (LifecycleListener listener : listeners) {
listener.beforeClose();
}
doClose();
for (LifecycleListener listener : listeners) {
listener.afterClose();
}
}
protected abstract void doClose() throws ElasticSearchException;

View File

@ -28,6 +28,10 @@ public interface LifecycleComponent<T> extends CloseableComponent {
Lifecycle.State lifecycleState();
void addLifecycleListener(LifecycleListener listener);
void removeLifecycleListener(LifecycleListener listener);
T start() throws ElasticSearchException;
T stop() throws ElasticSearchException;

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.util.component;
/**
* @author kimchy (shay.banon)
*/
public interface LifecycleListener {
void beforeStart();
void afterStart();
void beforeStop();
void afterStop();
void beforeClose();
void afterClose();
}

View File

@ -0,0 +1,74 @@
dependsOn(':elasticsearch')
usePlugin 'java'
archivesBaseName = "elasticsearch-attachments"
explodedDistDir = new File(distsDir, 'exploded')
manifest.mainAttributes("Implementation-Title": "ElasticSearch::Plugins::Attachments", "Implementation-Version": rootProject.version, "Implementation-Date": buildTimeStr)
// no need to use the resource dir
sourceSets.main.resources.srcDir 'src/main/java'
sourceSets.test.resources.srcDir 'src/test/java'
dependencies {
compile project(':elasticsearch')
compile('org.apache.tika:tika-app:0.6') { transitive = false }
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"]
options.suiteName = project.name
options.listeners = ["org.elasticsearch.util.testng.Listeners"]
options.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.compile
into explodedDistDir
}
// remove elasticsearch files (compile above adds the elasticsearch one)
ant.delete { fileset(dir: explodedDistDir, includes: "elasticsearch-*.jar") }
copy {
from libsDir
into explodedDistDir
}
}
task zip(type: Zip) {
dependsOn explodedDist
// classifier = 'all'
}
zip.doFirst {task ->
zipRootFolder = "" // its the plugin, don't create it under a specific name
task.configure {
zipFileSet(dir: explodedDistDir, prefix: zipRootFolder) {
// just copy over everything
}
}
}
task release(dependsOn: [zip]) << {
ant.delete(dir: explodedDistDir)
copy {
from distsDir
into(new File(rootProject.distsDir, "plugins"))
}
}

View File

@ -0,0 +1 @@
plugin=org.elasticsearch.plugin.attachments.AttachmentsPlugin

View File

@ -0,0 +1,32 @@
/*
* 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.plugin.attachments;
import org.elasticsearch.plugins.AbstractPlugin;
/**
* @author kimchy (shay.banon)
*/
public class AttachmentsPlugin extends AbstractPlugin {
@Override public String name() {
return "attachments";
}
}

View File

@ -6,9 +6,15 @@ include 'test-integration'
include 'benchmark-micro'
include 'plugins-attachments'
rootProject.name = 'elasticsearch-root'
rootProject.children.each {project ->
String fileBaseName = project.name.replaceAll("\\p{Upper}") { "-${it.toLowerCase()}" }
fileBaseName = fileBaseName.replace('-', '/');
project.projectDir = new File(settingsDir, "modules/$fileBaseName")
if (fileBaseName.startsWith("plugins")) {
project.projectDir = new File(settingsDir, "$fileBaseName")
} else {
project.projectDir = new File(settingsDir, "modules/$fileBaseName")
}
}