Index: conf/nutch-default.xml =================================================================== --- conf/nutch-default.xml (revision 395865) +++ conf/nutch-default.xml (working copy) @@ -564,7 +564,7 @@ plugin.includes - protocol-http|urlfilter-regex|parse-(text|html|js)|index-basic|query-(basic|site|url) + admin-(configuration|system|inject|job|listing|management|instance|scheduling|crawldb-status)|protocol-http|urlfilter-regex|parse-(text|html|js)|index-basic|query-(basic|site|url) Regular expression naming plugin directory names to include. Any plugin not matching this expression is excluded. In any case you need at least include the nutch-extensionpoints plugin. By @@ -806,4 +806,27 @@ + + + admin.gui.port + 50060 + Port used for the embedded webcontainer that + runs the administration gui. + + + + admin.gui.realm + org.apache.nutch.admin.DefaultRealm + A class provides user lookup and authentication methods. + See org.apache.nutch.admin.DefaultRealm for details. + + + + + admin.gui.realm.password + admin + A password used by the DefaultRealm implementation. + + + Index: src/java/org/apache/nutch/admin/WebContainer.java =================================================================== --- src/java/org/apache/nutch/admin/WebContainer.java (revision 0) +++ src/java/org/apache/nutch/admin/WebContainer.java (revision 0) @@ -0,0 +1,184 @@ +/** + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nutch.admin; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.logging.Logger; + +import javax.servlet.Servlet; + +import org.apache.hadoop.conf.Configurable; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.util.LogFormatter; +import org.apache.nutch.plugin.Extension; +import org.apache.nutch.plugin.PluginRuntimeException; +import org.mortbay.http.BasicAuthenticator; +import org.mortbay.http.SecurityConstraint; +import org.mortbay.http.SocketListener; +import org.mortbay.http.UserRealm; +import org.mortbay.http.handler.SecurityHandler; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.WebApplicationContext; + +/** + * Embed {@link Servlet} container. + */ +public class WebContainer extends Thread { + + private static final Logger LOG = + LogFormatter.getLogger(WebContainer.class.getName()); + + private Server fServer; + + public WebContainer(int port, Configuration configuration) { + this.fServer = new Server(); + String className = configuration.get("admin.gui.realm", + "org.apache.nutch.admin.DefaultRealm"); + + try { + Configurable realm = (Configurable) Class.forName(className).newInstance(); + realm.setConf(configuration); + this.fServer.addRealm((UserRealm) realm); + } catch (Exception e) { + LOG.fine("unable to add realm: " + e.toString()); + } + + SocketListener listener = new SocketListener(); + listener.setPort(port); + this.fServer.addListener(listener); + } + + public void run() { + try { + this.fServer.start(); + } catch (Exception e) { + LOG.fine(e.toString()); + } + } + + /** + * starts the web container. + * @throws IOException if container couldn't started + */ + public void startContainer() throws IOException { + start(); + try { + Thread.sleep(3000); + } catch (InterruptedException ie) {} + if (this.fServer == null || !this.fServer.isStarted()) { + throw new IOException("Could not start web container"); + } + } + + /** + * stops the web container + * @throws InterruptedException + */ + public void stopContainer() throws InterruptedException { + if (this.fServer != null && this.fServer.isStarted()) { + this.fServer.stop(); + } + } + + + /** + * Deploys a set of {@link GuiComponent} extentsions as web applications. + * + * @param extensions + * @param theInstance + * @param allInstances + * @throws Exception + */ + public void addComponentExtensions(Extension[] extensions, + NutchInstance theInstance, NutchInstance[] allInstances) throws Exception { + + // Instantiate and configure + ArrayList componentList = new ArrayList(); + + for (int i = 0; i < extensions.length; i++) { + try { + Extension extension = extensions[i]; + GuiComponent component = + (GuiComponent) extension.getExtensionInstance(); + + component.configure(extension, theInstance); + componentList.add(component); + + } catch (PluginRuntimeException e) { + LOG.fine(e.toString()); + } + } + GuiComponent[] components = (GuiComponent[]) + componentList.toArray(new GuiComponent[componentList.size()]); + + for (int i = 0; i < components.length; i++) { + GuiComponent component = components[i]; + Extension extension = component.getExtension(); + String jspFolder = extension.getAttribute(GuiComponent.JSP_FOLDER); + + if (jspFolder == null) { + jspFolder = "jsp"; + } + + String jsps = + extension.getDescriptor().getPluginPath() + + File.separator + + jspFolder + + File.separator; + + String contextName = ""; + String plugName = extension.getDescriptor().getPluginId(); + + if (plugName.equals("admin-listing")) { + contextName = theInstance.getName() + "/"; + } else { + contextName = + theInstance.getName() + + "/" + + extension.getDescriptor().getPluginId(); + } + + WebApplicationContext webContext = + this.fServer.addWebApplication( + contextName, + new File(jsps).getCanonicalPath() + ); + + webContext.setClassLoader(extension.getDescriptor().getClassLoader()); + webContext.setAttribute("component", component); + webContext.setAttribute("components", components); + if (allInstances != null) { + webContext.setAttribute("instances", allInstances); + webContext.setAttribute("container", this); + } + + SecurityHandler handler = new SecurityHandler(); + handler.setAuthMethod("BASIC"); + webContext.addHandler(handler); + webContext.setAuthenticator(new BasicAuthenticator()); + SecurityConstraint sc = new SecurityConstraint(); + sc.setAuthenticate(true); + sc.addRole(SecurityConstraint.ANY_ROLE); + webContext.addSecurityConstraint("/", sc); + + webContext.start(); + } + } + +} Index: src/java/org/apache/nutch/admin/DefaultRealm.java =================================================================== --- src/java/org/apache/nutch/admin/DefaultRealm.java (revision 0) +++ src/java/org/apache/nutch/admin/DefaultRealm.java (revision 0) @@ -0,0 +1,47 @@ +/** + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nutch.admin; + +import org.apache.hadoop.conf.Configurable; +import org.apache.hadoop.conf.Configuration; +import org.mortbay.http.HashUserRealm; +import org.mortbay.http.UserRealm; + +/** + * A simple Realm implementation that adds just one user "admin" + * and looks up a password from the set configuration. + * + * Nutch realms need to implement {@link Configurable} and + * {@link UserRealm}. + * + */ +public class DefaultRealm extends HashUserRealm implements Configurable { + + private Configuration fConf; + + public void setConf(Configuration conf) { + this.fConf = conf; + String password = conf.get("admin.gui.realm.password", "admin"); + put("admin", password); + + } + + public Configuration getConf() { + return this.fConf; + } + +} Index: src/java/org/apache/nutch/admin/AdministrationApp.java =================================================================== --- src/java/org/apache/nutch/admin/AdministrationApp.java (revision 0) +++ src/java/org/apache/nutch/admin/AdministrationApp.java (revision 0) @@ -0,0 +1,201 @@ +/** + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nutch.admin; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.logging.Logger; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.mapred.JobTracker; +import org.apache.hadoop.util.LogFormatter; +import org.apache.nutch.plugin.Extension; +import org.apache.nutch.plugin.ExtensionPoint; +import org.apache.nutch.plugin.PluginRepository; +import org.apache.nutch.util.NutchConfiguration; + +/** + * Administration Application + */ +public class AdministrationApp { + + + private static final Logger LOG = + LogFormatter.getLogger(AdministrationApp.class.getName()); + + + private void startJobTracker(final Configuration defaultConf) { + Runnable jobTrackerStarter = new Runnable() { + public void run() { + try { + String jobtracker = defaultConf.get("mapred.job.tracker", "local"); + if (!"local".equals(jobtracker)) { + JobTracker.startTracker(NutchConfiguration.create()); + Thread.sleep(3000); + } + } catch (IOException e) { + LOG.warning(e.toString()); + } catch (InterruptedException e) { + LOG.warning(e.toString()); + } + } + }; + Thread t = new Thread(jobTrackerStarter); + t.start(); + } + + /** + * starts a container and deploys all gui plugins + */ + public WebContainer startContainer(File initialInstance, Configuration defaultConf) throws Exception { + + + int port = defaultConf.getInt("admin.gui.port", 50060); + WebContainer webContainer = new WebContainer(port, defaultConf); + webContainer.startContainer(); + + NutchInstance[] nutchInstances = getInstances(defaultConf, initialInstance); + // add all general-components + Extension[] generalGuiComponents = + getComponentExtensions(defaultConf, GuiComponent.IS_GENERAL_COMPONENT); + NutchInstance generalInstance = + new NutchInstance("general", initialInstance, defaultConf); + webContainer.addComponentExtensions( + generalGuiComponents, generalInstance, nutchInstances); + + // add instance-components + for (int i = 0; i < nutchInstances.length; i++) { + NutchInstance instance = nutchInstances[i]; + Extension[] extensions = getComponentExtensions( + instance.getConfiguration(), GuiComponent.IS_INSTANCE_COMPONENT); + webContainer.addComponentExtensions(extensions, instance, null); + } + return webContainer; + } + + /** + * @param conf + * @param attributeName + * attribute value must be set to "true" in plugin.xml + * @return extensions implementing {@link GuiComponent} + * and matching the attribute filter + */ + public static Extension[] getComponentExtensions(Configuration conf, + String attributeName) { + ArrayList list = new ArrayList(); + ExtensionPoint extensionPoint = + PluginRepository.get(conf).getExtensionPoint(GuiComponent.X_POINT_ID); + if (extensionPoint == null) { + throw new RuntimeException("x-point " + + GuiComponent.X_POINT_ID + + " not found, check your plugin folder"); + } + Extension[] extensions = extensionPoint.getExtensions(); + for (int i = 0; i < extensions.length; i++) { + Extension extension = extensions[i]; + if (extension.getAttribute(attributeName) != null + && extension.getAttribute(attributeName).toLowerCase().equals("true")) { + list.add(extension); + } + } + return (Extension[]) list.toArray(new Extension[list.size()]); + + } + + /* scans the root folder for instance folders */ + private NutchInstance[] getInstances(Configuration defaultConf, File instancesRoot) { + File[] files = instancesRoot.listFiles(); + ArrayList instancesList = new ArrayList(); + for (int i = 0; i < files.length; i++) { + File folder = files[i]; + if (folder.isDirectory() && !folder.getName().equals("conf")) { + try { + instancesList.add(loadNutchInstance(defaultConf, folder)); + } catch (IOException e) { + LOG.fine("unable to load instance: " + e.toString()); + } + } + } + return (NutchInstance[]) + instancesList.toArray(new NutchInstance[instancesList.size()]); + } + + /** + * creates an instance object from a instance folder + * + * @param defaultConf + * @param folder + * @return an instance representation of this folder + * @throws IOException + * in case the folder is not a valid instance folder + */ + public static NutchInstance loadNutchInstance(Configuration defaultConf, File folder) + throws IOException { + File instanceConfFolder = new File(folder, "conf"); + if (instanceConfFolder.exists() && instanceConfFolder.isDirectory()) { + File instanceSiteConf = new File(instanceConfFolder, "nutch-site.xml"); + if (instanceSiteConf.exists()) { + Configuration instanceConf = new Configuration(defaultConf); + instanceConf.addFinalResource(instanceSiteConf.getAbsolutePath()); + return new NutchInstance(folder.getName(), folder, instanceConf); + } + } + throw new IOException("not a valid instance folder: " + + folder.getAbsolutePath()); + } + + private void createFirstInstance(File file) throws IOException { + GuiConfigUtil.createConfiguration(file); + File defaultInstance = new File(file, "default"); + GuiConfigUtil.createConfiguration(defaultInstance); + } + + + /** + * Starts the nutch administration web interface + * + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + String usage = "Usage: "; + if (args.length != 1) { + System.err.println(usage); + return; + } + AdministrationApp app = new AdministrationApp(); + File file = new File(args[0]); + if (!file.exists()) { + app.createFirstInstance(file); + } + Configuration defaultConf = + GuiConfigUtil.loadNewConfiguration(file); + app.startJobTracker(defaultConf); + + try { + WebContainer container = app.startContainer(file, defaultConf); + container.join(); + } catch (Exception e) { + LOG.fine(e.getMessage()); + System.err.println(usage); + } + } + + + +} Index: src/java/org/apache/nutch/admin/NutchInstance.java =================================================================== --- src/java/org/apache/nutch/admin/NutchInstance.java (revision 0) +++ src/java/org/apache/nutch/admin/NutchInstance.java (revision 0) @@ -0,0 +1,52 @@ +/** + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nutch.admin; + +import java.io.File; + +import org.apache.hadoop.conf.Configuration; + +/** + * Holds instance related properties. + */ +public class NutchInstance { + + private File fInstanceFolder; + + private Configuration fInstanceConf; + + private String fInstanceName; + + public NutchInstance(String name, File folder, Configuration instanceConf) { + this.fInstanceName = name; + this.fInstanceFolder = folder; + this.fInstanceConf = instanceConf; + } + + public String getName() { + return fInstanceName; + } + + public Configuration getConfiguration() { + return this.fInstanceConf; + } + + public File getInstanceFolder() { + return fInstanceFolder; + } + +} Index: src/java/org/apache/nutch/admin/GuiComponent.java =================================================================== --- src/java/org/apache/nutch/admin/GuiComponent.java (revision 0) +++ src/java/org/apache/nutch/admin/GuiComponent.java (revision 0) @@ -0,0 +1,108 @@ +/** + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nutch.admin; + +import java.util.Locale; + +import org.apache.nutch.plugin.Extension; + +/** + * An Extension point definition for functional administration interface + * components. + * + * The nutch functional administration interface is built form a set of plug-ins + * that are plugged together into a embedded web container. The extension point, + * the plug-ins have to implement, is named GuiComponent. + * + * To be able to manage multiple different configured deployments (e.g. intranet + * and home-page) we introduce the concept of so called instances. Instances use + * the same nutch code base and one general configuration, but can have + * properties that overwrite the general configured properties. Also each + * instance has a own data folder (local or dfs) where the databases, segments + * and indexes are stored. + * + * Since there is a general nutch configuration and several nutch instances we + * have two kind of plug-ins: general plug-ins and instance plug-ins. Instance + * plug-ins only have access to the instance- folder and configuration but + * general plug-ins have access to all instances and the general configuration. + * + * To define a plug-in as general- or instance-depending, the attributes + * "isGeneralComponent" or "isInstanceComponent" can be defined as true. It is + * also possible, that a plugin can be deployed in both contexts. + * + * + * The most important part of an administration interface plugin are jsp pages. + * Each plugin need at least one index.jsp page that is located in a folder that + * can be defined in the plugin.xml as attribute "jspFolder" of the extension + * node. Also a attribute "tabName" is required, where the value is used to show + * the component index page in a navigation bar. + * + * To make administration interface plugin development easier, we provide a + * DefaultGuiComponent that handles all functionalities and + * provides internationalization support. So only the + * DefaultGuiComponent definition in the plugin.xml, required attributes, a + * set of jsp pages and i18n bundles need to be defined to get a component + * implemented. + */ +public interface GuiComponent { + + /** extension point id */ + public final static String X_POINT_ID = GuiComponent.class.getName(); + + /** + * Attribute determinate if a component is general. + */ + public static final String IS_GENERAL_COMPONENT = "isGeneralComponent"; + + /** + * Attribute determinate if a component is instance specific. + */ + public static final String IS_INSTANCE_COMPONENT = "isInstanceComponent"; + + /** + * Attribute defines the folder inside the plugin folder contains jsp and jsp + * snippets. + */ + public static final String JSP_FOLDER = "jspFolder"; + + /** + * Configures a component. Method is only triggered once until gui startup + * + * @param extension + * providing access to plugin.xml attributes + * @param instance + * providing access to configuration properties + */ + public void configure(Extension extension, NutchInstance instance); + + /** + * @param key + * @param locale + * @return localized values + */ + public String getLabel(String key, Locale locale); + + /** + * @return via configure injected extension + */ + public Extension getExtension(); + + /** + * @return via configure injected instance + */ + public NutchInstance getNutchInstance(); +} Index: src/java/org/apache/nutch/admin/GuiConfigUtil.java =================================================================== --- src/java/org/apache/nutch/admin/GuiConfigUtil.java (revision 0) +++ src/java/org/apache/nutch/admin/GuiConfigUtil.java (revision 0) @@ -0,0 +1,113 @@ +/** + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nutch.admin; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.nutch.util.NutchConfiguration; + +/** + * Bundles some methods to handle configuration files + */ +public class GuiConfigUtil { + + /** + * @param folder + * @return fresh loaded configuration + */ + public static Configuration loadNewConfiguration(File folder) { + Configuration configuration = NutchConfiguration.create(); + configure(configuration, folder); + return configuration; + } + + + /** + * Creates a configuration folder and stores nutch's configuration files + * in this folder. + * + * @param instanceFolder + * @throws IOException + */ + public static void createConfiguration(File instanceFolder) + throws IOException { + + File confFolder = new File(instanceFolder, "conf"); + confFolder.mkdirs(); + copyConfigurationFiles(confFolder); + } + + private static void copyConfigurationFiles(File target) + throws FileNotFoundException, IOException { + + InputStream in = + AdministrationApp.class.getResourceAsStream("/nutch-default.xml"); + OutputStream out = + new FileOutputStream(new File(target,"nutch-default.xml")); + + copyContent(in, out); + + in = AdministrationApp.class.getResourceAsStream("/nutch-site.xml"); + out = new FileOutputStream(new File(target, "nutch-site.xml")); + + copyContent(in, out); + } + + private static void copyContent(InputStream in, OutputStream out) throws IOException { + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.flush(); + in.close(); + out.close(); + } + + /** + * Push nutch-(default|site).xml from a given folder/conf + * into a configuration. + * + * @param configuration + * @param folder + */ + private static void configure(Configuration configuration, File folder) { + + File confFolder = new File(folder, "conf"); + + if (confFolder.exists()) { + + File defaultConf = new File(confFolder, "nutch-default.xml"); + if (defaultConf.exists()) { + configuration.addDefaultResource(new Path(defaultConf.getAbsolutePath())); + } + + File siteConf = new File(confFolder, "nutch-site.xml"); + if (siteConf.exists()) { + configuration.addFinalResource(new Path(siteConf.getAbsolutePath())); + } + + } + } +} Index: src/java/org/apache/nutch/admin/TaskThread.java =================================================================== --- src/java/org/apache/nutch/admin/TaskThread.java (revision 0) +++ src/java/org/apache/nutch/admin/TaskThread.java (revision 0) @@ -0,0 +1,40 @@ +/** + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nutch.admin; + +import org.apache.hadoop.conf.Configuration; + + +/** + * Abstract Thread used to run processes in the gui and providing process messages. + */ +public abstract class TaskThread extends Thread { + + protected String fMessage; + + protected Configuration fConfiguration; + + public TaskThread(Configuration configuration) { + this.fConfiguration = configuration; + } + + public String getMessage() { + return this.fMessage; + } + + public abstract void run(); +} Index: src/java/org/apache/nutch/admin/DefaultGuiComponent.java =================================================================== --- src/java/org/apache/nutch/admin/DefaultGuiComponent.java (revision 0) +++ src/java/org/apache/nutch/admin/DefaultGuiComponent.java (revision 0) @@ -0,0 +1,72 @@ +/** + * Copyright 2005 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nutch.admin; + +import java.util.HashMap; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.logging.Logger; + +import org.apache.nutch.plugin.Extension; +import org.apache.nutch.plugin.PluginClassLoader; + +/** + * A default implementation of the {@link GuiComponent} extension point. + */ +public class DefaultGuiComponent implements GuiComponent { + + private static final Logger LOG = + Logger.getLogger(DefaultGuiComponent.class.getName()); + + private HashMap fResourceBundles = new HashMap(); + + private Extension fExtension; + + private NutchInstance fNutchInstance; + + public void configure(Extension extension, NutchInstance instance) { + this.fExtension = extension; + this.fNutchInstance = instance; + } + + public String getLabel(String key, Locale locale) { + String value = key; + ResourceBundle labels = (ResourceBundle) this.fResourceBundles.get(locale); + if (labels == null) { + try { + PluginClassLoader classLoader = + this.fExtension.getDescriptor().getClassLoader(); + String bundleName = this.fExtension.getAttribute("bundle"); + labels = ResourceBundle.getBundle(bundleName, locale, classLoader); + this.fResourceBundles.put(locale, labels); + } catch (Exception e) { + LOG.warning("unable to load resource bundle: "+e.toString()); + } + } + value = labels.getString(key); + return value; + } + + public NutchInstance getNutchInstance() { + return this.fNutchInstance; + } + + public Extension getExtension() { + return this.fExtension; + } + +} Index: src/plugin/build.xml =================================================================== --- src/plugin/build.xml (revision 395865) +++ src/plugin/build.xml (working copy) @@ -52,6 +52,15 @@ + + + + + + + + + @@ -126,6 +135,15 @@ + + + + + + + + + Index: src/plugin/nutch-extensionpoints/plugin.xml =================================================================== --- src/plugin/nutch-extensionpoints/plugin.xml (revision 395865) +++ src/plugin/nutch-extensionpoints/plugin.xml (working copy) @@ -44,5 +44,9 @@ + + Index: bin/nutch =================================================================== --- bin/nutch (revision 395865) +++ bin/nutch (working copy) @@ -43,6 +43,7 @@ echo " dedup remove duplicates from a set of segment indexes" echo " plugin load a plugin and run one of its classes main()" echo " server run a search server" + echo " gui run the Administration Gui" echo " or" echo " CLASSNAME run the class named CLASSNAME" echo "Most commands print help when invoked w/o parameters." @@ -149,6 +150,8 @@ CLASS=org.apache.nutch.plugin.PluginRepository elif [ "$COMMAND" = "server" ] ; then CLASS='org.apache.nutch.searcher.DistributedSearch$Server' +elif [ "$COMMAND" = "gui" ] ; then + CLASS='org.apache.nutch.admin.AdministrationApp' else CLASS=$COMMAND fi