You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by po...@apache.org on 2017/09/03 17:33:13 UTC

[20/51] [partial] incubator-netbeans-jackpot30 git commit: INFRA-15006 Import for http://bits.netbeans.org/download/apache-donation

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsages.java
----------------------------------------------------------------------
diff --git a/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsages.java b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsages.java
new file mode 100644
index 0000000..06bbd03
--- /dev/null
+++ b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsages.java
@@ -0,0 +1,491 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.jackpot30.ide.usages;
+
+import com.sun.source.util.TreePath;
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Dialog;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.border.EmptyBorder;
+import javax.swing.text.JTextComponent;
+import org.codeviation.pojson.Pojson;
+import org.netbeans.api.editor.EditorRegistry;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.SourceUtils;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.ui.ElementHeaders;
+import org.netbeans.api.java.source.ui.ScanDialog;
+import org.netbeans.modules.editor.NbEditorUtilities;
+import org.netbeans.modules.jackpot30.common.api.JavaUtils;
+import org.netbeans.modules.jackpot30.remoting.api.RemoteIndex;
+import org.netbeans.modules.jackpot30.remoting.api.Utilities;
+import org.netbeans.modules.jackpot30.remoting.api.Utilities.RemoteSourceDescription;
+import org.netbeans.modules.jackpot30.remoting.api.WebUtilities;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.NotifyDescriptor.Message;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.awt.ActionRegistration;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.nodes.Node;
+import org.openide.util.Cancellable;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle.Messages;
+import org.openide.util.RequestProcessor;
+
+@ActionID(category = "Refactoring",
+id = "org.netbeans.modules.jackpot30.ide.usages.RemoteUsages")
+@ActionRegistration(displayName = "#CTL_RemoteUsages")
+@ActionReferences({
+    @ActionReference(path = "Menu/Edit", position = 2250)
+})
+@Messages("CTL_RemoteUsages=Find Remote Usages...")
+public final class RemoteUsages implements ActionListener {
+
+    private final RequestProcessor WORKER = new RequestProcessor(RemoteUsages.class.getName(), 1, false, false);
+    
+    public void actionPerformed(ActionEvent e) {
+        JTextComponent comp = EditorRegistry.lastFocusedComponent(); //XXX
+
+        if (comp == null) return;
+
+        final FileObject file = NbEditorUtilities.getFileObject(comp.getDocument());
+        final int pos = comp.getCaretPosition();
+        final ElementDescription element = findElement(file, pos);
+
+        if (element == null) {
+            Message message = new NotifyDescriptor.Message("Cannot find usages of this element", NotifyDescriptor.Message.ERROR_MESSAGE);
+            DialogDisplayer.getDefault().notifyLater(message);
+            return ;
+        }
+
+        final Set<SearchOptions> options = EnumSet.noneOf(SearchOptions.class);
+        final JButton okButton = new JButton("OK");
+        JButton cancelButton = new JButton("Cancel");
+        final ElementHandle[] searchFor = new ElementHandle[1];
+        JPanel dialogContent = constructDialog(element, options, new SelectionListener() {
+            @Override
+            public void elementSelected(ElementHandle<?> selected) {
+                searchFor[0] = selected;
+            }
+        }, okButton);
+
+        DialogDescriptor dd = new DialogDescriptor(dialogContent, "Remote Find Usages", true, new Object[] {okButton, cancelButton}, okButton, DialogDescriptor.DEFAULT_ALIGN, null, new ActionListener() {
+            @Override public void actionPerformed(ActionEvent e) { }
+        });
+        final Dialog d = DialogDisplayer.getDefault().createDialog(dd);
+
+        final AtomicBoolean cancel = new AtomicBoolean();
+
+        okButton.addActionListener(new ActionListener() {
+            @Override public void actionPerformed(ActionEvent e) {
+                okButton.setEnabled(false);
+                WORKER.post(new FindUsagesWorker(searchFor[0], options, d, cancel));
+            }
+        });
+
+        cancelButton.addActionListener(new ActionListener() {
+            @Override public void actionPerformed(ActionEvent e) {
+                cancel.set(true);
+                d.setVisible(false);
+            }
+        });
+
+        d.setVisible(true);
+    }
+
+    private static ElementDescription findElement(final FileObject file, final int pos) {
+        final ElementDescription[] handle = new ElementDescription[1];
+
+        if ("text/x-java".equals(FileUtil.getMIMEType(file, "text/x-java"))) {
+            final JavaSource js = JavaSource.forFileObject(file);
+
+            ScanDialog.runWhenScanFinished(new Runnable() {
+                @Override public void run() {
+                    try {
+                        js.runUserActionTask(new Task<CompilationController>() {
+                            @Override public void run(CompilationController parameter) throws Exception {
+                                parameter.toPhase(JavaSource.Phase.RESOLVED);
+
+                                TreePath tp = parameter.getTreeUtilities().pathFor(pos);
+                                Element el = parameter.getTrees().getElement(tp);
+
+                                if (el != null && JavaUtils.SUPPORTED_KINDS.contains(el.getKind())) {
+                                    handle[0] = new ElementDescription(parameter, el);
+                                }
+                            }
+                        }, true);
+                    } catch (IOException ex) {
+                        Exceptions.printStackTrace(ex);
+                    }
+                }
+
+            }, "Find Remote Usages");
+
+            return handle[0];
+        } else {
+            RemoteSourceDescription rsd = org.netbeans.modules.jackpot30.remoting.api.Utilities.remoteSource(file);
+
+            if (rsd != null) {
+                try {
+                    URI sourceURI = new URI(rsd.idx.remote.toExternalForm() + "/ui/target?path=" + WebUtilities.escapeForQuery(rsd.idx.remoteSegment) + "&relative=" + WebUtilities.escapeForQuery(rsd.relative) + "&position=" + pos);
+                    Map<Object, Object> targetData = Pojson.load(HashMap.class, sourceURI.toURL().openStream());
+
+                    String signature = (String) targetData.get("signature");
+
+                    if (signature != null) {
+                        List<String> baseMethodsSpec = (List<String>) targetData.get("superMethods");
+                        baseMethodsSpec = baseMethodsSpec != null ? baseMethodsSpec : Collections.<String>emptyList();
+                        List<ElementHandle<?>> baseMethods = new ArrayList<ElementHandle<?>>(baseMethodsSpec.size());
+                        for (String spec : baseMethodsSpec) {
+                            baseMethods.add(signature2Handle(spec));
+                        }
+                        return new ElementDescription(signature2Handle(signature),
+                                                      baseMethods);
+                    }
+                } catch (URISyntaxException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+
+            return null;
+        }
+    }
+
+    private static ElementHandle<?> signature2Handle(String signature) {
+        if (signature == null) return null;
+        String[] parts = signature.split(":");
+        ElementHandle<?> h = Utilities.createElementHandle(ElementKind.valueOf(parts[0]),
+                                                           parts[1],
+                                                           parts.length > 2 ? parts[2] : null,
+                                                           parts.length > 3 ? parts[3] : null);
+        return h;
+    }
+
+    private JPanel constructDialog(ElementDescription toSearch, Set<SearchOptions> options, SelectionListener sl, JButton ok) {
+        JPanel searchKind;
+
+        switch (toSearch.element.getKind()) {
+            case METHOD: searchKind = new MethodOptions(toSearch, options, sl); break;
+            case CLASS:
+            case INTERFACE:
+            case ANNOTATION_TYPE: searchKind = new ClassOptions(options); break;
+            default:
+                options.add(RemoteUsages.SearchOptions.USAGES);
+                searchKind = new JPanel();
+                break;
+        }
+        
+        final JPanel progress = new JPanel();
+
+        progress.setLayout(new CardLayout());
+        progress.add(new JPanel(), "hide");
+        progress.add(new JLabel("Querying remote server(s), please wait"), "show");
+
+        ok.addActionListener(new ActionListener() {
+            @Override public void actionPerformed(ActionEvent e) {
+                ((CardLayout) progress.getLayout()).show(progress, "show");
+            }
+        });
+
+        JPanel result = new JPanel();
+
+        result.setLayout(new BorderLayout());
+        result.setBorder(new EmptyBorder(new Insets(12, 12, 12, 12)));
+
+        result.add(new JLabel("Usages of: " + toSearch.displayName), BorderLayout.NORTH);
+        result.add(searchKind, BorderLayout.CENTER);
+        result.add(progress, BorderLayout.SOUTH);
+
+        sl.elementSelected(toSearch.element);
+        
+        return result;
+    }
+    
+    public static final class ElementDescription {
+        public final ElementHandle<?> element;
+        public final String displayName;
+        public final List<ElementHandle<?>> superMethods;
+
+        public ElementDescription(CompilationInfo info, Element el) {
+            element = ElementHandle.create(el);
+            displayName = displayNameForElement(ElementHandle.create(el));
+
+            if (el.getKind() == ElementKind.METHOD) {
+                superMethods = superMethods(info, new HashSet<TypeElement>(), (ExecutableElement) el, (TypeElement) el.getEnclosingElement());
+            } else {
+                superMethods = null;
+            }
+        }
+
+        private List<ElementHandle<?>> superMethods(CompilationInfo info, Set<TypeElement> seenTypes, ExecutableElement baseMethod, TypeElement currentType) {
+            if (!seenTypes.add(currentType))
+                return Collections.emptyList();
+
+            List<ElementHandle<?>> result = new ArrayList<ElementHandle<?>>();
+
+            for (TypeElement sup : superTypes(info, currentType)) {
+                for (ExecutableElement ee : ElementFilter.methodsIn(sup.getEnclosedElements())) {
+                    if (info.getElements().overrides(baseMethod, ee, (TypeElement) baseMethod.getEnclosingElement())) {
+                        result.add(ElementHandle.create(ee));
+                    }
+                }
+
+                result.addAll(superMethods(info, seenTypes, baseMethod, currentType));
+            }
+            
+            return result;
+        }
+
+        private List<TypeElement> superTypes(CompilationInfo info, TypeElement type) {
+            List<TypeElement> superTypes = new ArrayList<TypeElement>();
+
+            for (TypeMirror sup : info.getTypes().directSupertypes(type.asType())) {
+                if (sup.getKind() == TypeKind.DECLARED) {
+                    superTypes.add((TypeElement) ((DeclaredType) sup).asElement());
+                }
+            }
+
+            return superTypes;
+        }
+
+        public ElementDescription(ElementHandle<?> element, List<ElementHandle<?>> superMethods) {
+            this.element = element;
+            displayName = displayNameForElement(element);
+            this.superMethods = superMethods;
+        }
+
+        private String displayNameForElement(ElementHandle<?> el) throws UnsupportedOperationException {
+            String[] signatures = SourceUtils.getJVMSignature(el);
+            String classSimpleName = signatures[0];
+            int lastDotDollar = Math.max(classSimpleName.lastIndexOf('.'), classSimpleName.lastIndexOf('$'));
+            if (lastDotDollar > (-1)) classSimpleName = classSimpleName.substring(lastDotDollar + 1);
+            switch (el.getKind()) {
+                case METHOD:
+                    return signatures[1] + Utilities.decodeMethodParameterTypes(signatures[2]);
+                case CONSTRUCTOR:
+                    return classSimpleName + Utilities.decodeMethodParameterTypes(signatures[2]);
+                case CLASS:
+                case INTERFACE:
+                case ENUM:
+                case ANNOTATION_TYPE:
+                    return classSimpleName;
+                case FIELD:
+                case ENUM_CONSTANT:
+                    return signatures[1];
+                default:
+                    throw new UnsupportedOperationException();
+            }
+        }
+
+    }
+
+    private static class FindUsagesWorker implements Runnable, Cancellable {
+        
+        private final ElementHandle<?> toSearch;
+        private final Set<SearchOptions> options;
+        private final Dialog d;
+        private final AtomicBoolean cancel;
+
+        public FindUsagesWorker(ElementHandle<?> toSearch, Set<SearchOptions> options, Dialog d, AtomicBoolean cancel) {
+            this.toSearch = toSearch;
+            this.options = options;
+            this.d = d;
+            this.cancel = cancel;
+        }
+
+        @Override public void run() {
+            try {
+                final String serialized = JavaUtils.serialize(toSearch);
+
+                Set<FileObject> resultSet = new HashSet<FileObject>();
+                List<FileObject> result = new ArrayList<FileObject>();
+                Map<RemoteIndex, List<String>> unmappable = new HashMap<RemoteIndex, List<String>>();
+
+                for (RemoteIndex idx : RemoteIndex.loadIndices()) {
+                    URL localFolderURL = idx.getLocalFolder();
+                    FileObject localFolder = localFolderURL != null ? URLMapper.findFileObject(localFolderURL) : null;
+
+                    if (options.contains(SearchOptions.USAGES)) {
+                        URI resolved = new URI(idx.remote.toExternalForm() + "/usages/search?path=" + WebUtilities.escapeForQuery(idx.remoteSegment) + "&signatures=" + WebUtilities.escapeForQuery(serialized));
+                        Collection<? extends String> response = WebUtilities.requestStringArrayResponse(resolved, cancel);
+
+                        if (cancel.get()) return;
+                        if (response == null) continue;
+
+                        for (String path : response) {
+                            if (path.trim().isEmpty()) continue;
+                            FileObject file = localFolder != null ? localFolder.getFileObject(path) : null;
+
+                            if (file != null) {
+                                if (resultSet.add(file)) {
+                                    result.add(file);
+                                }
+                            } else {
+                                List<String> um = unmappable.get(idx);
+
+                                if (um == null) {
+                                    unmappable.put(idx, um = new ArrayList<String>());
+                                }
+
+                                um.add(path);
+                            }
+                        }
+                    }
+
+                    if (options.contains(SearchOptions.SUB)) {
+                        URI resolved;
+                        if (toSearch.getKind() == ElementKind.METHOD) {
+                            resolved = new URI(idx.remote.toExternalForm() + "/implements/search?path=" + WebUtilities.escapeForQuery(idx.remoteSegment) + "&method=" + WebUtilities.escapeForQuery(serialized));
+                        } else {
+                            resolved = new URI(idx.remote.toExternalForm() + "/implements/search?path=" + WebUtilities.escapeForQuery(idx.remoteSegment) + "&type=" + WebUtilities.escapeForQuery(toSearch.getBinaryName()));
+                        }
+
+                        String response = WebUtilities.requestStringResponse(resolved, cancel);
+
+                        if (cancel.get()) return;
+                        if (response == null) continue;
+
+                        //XXX:
+                        Map<String, List<Map<String, String>>> formattedResponse = Pojson.load(LinkedHashMap.class, response);
+
+                        for (Entry<String, List<Map<String, String>>> e : formattedResponse.entrySet()) {
+                            for (Map<String, String> p : e.getValue()) {
+                                String path = p.get("file");
+                                FileObject file = localFolder != null ? localFolder.getFileObject(path) : null;
+
+                                if (file != null) {
+                                    if (resultSet.add(file)) {
+                                        result.add(file);
+                                    }
+                                } else {
+                                    List<String> um = unmappable.get(idx);
+
+                                    if (um == null) {
+                                        unmappable.put(idx, um = new ArrayList<String>());
+                                    }
+
+                                    um.add(path);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                final Node view = Nodes.constructSemiLogicalView(result, unmappable, toSearch, options);
+
+                if (!cancel.get()) {
+                    SwingUtilities.invokeLater(new Runnable() {
+                        @Override public void run() {
+                            RemoteUsagesWindowTopComponent.openFor(view);
+                        }
+                    });
+                }
+            } catch (URISyntaxException ex) {
+                Exceptions.printStackTrace(ex);
+            } finally {
+                cancel.set(true);
+                SwingUtilities.invokeLater(new Runnable() {
+                    @Override public void run() {
+                        d.setVisible(false);
+                    }
+                });
+            }
+        }
+
+        @Override public boolean cancel() {
+            cancel.set(true);
+            return true;
+        }
+    }
+
+    public enum SearchOptions {
+        USAGES,
+        SUB;
+    }
+
+    public interface SelectionListener {
+        public void elementSelected(ElementHandle<?> selected);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsagesWindowTopComponent.form
----------------------------------------------------------------------
diff --git a/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsagesWindowTopComponent.form b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsagesWindowTopComponent.form
new file mode 100644
index 0000000..d0fffb9
--- /dev/null
+++ b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsagesWindowTopComponent.form
@@ -0,0 +1,17 @@
+<?xml version="1.1" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
+</Form>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsagesWindowTopComponent.java
----------------------------------------------------------------------
diff --git a/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsagesWindowTopComponent.java b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsagesWindowTopComponent.java
new file mode 100644
index 0000000..4ee2131
--- /dev/null
+++ b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsagesWindowTopComponent.java
@@ -0,0 +1,147 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.jackpot30.ide.usages;
+
+import java.awt.BorderLayout;
+import org.openide.util.NbBundle;
+import org.openide.windows.Mode;
+import org.openide.windows.TopComponent;
+import org.netbeans.api.settings.ConvertAsProperties;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.explorer.ExplorerManager;
+import org.openide.explorer.view.BeanTreeView;
+import org.openide.nodes.Node;
+import org.openide.windows.WindowManager;
+
+/**
+ * Top component which displays something.
+ */
+@ConvertAsProperties(dtd = "-//org.netbeans.modules.jackpot30.ide.usages//RemoteUsagesWindow//EN",
+autostore = false)
+@TopComponent.Description(preferredID = "RemoteUsagesWindowTopComponent",
+//iconBase="SET/PATH/TO/ICON/HERE", 
+persistenceType = TopComponent.PERSISTENCE_ALWAYS)
+@TopComponent.Registration(mode = "output", openAtStartup = false)
+@ActionID(category = "Window", id = "org.netbeans.modules.jackpot30.ide.usages.RemoteUsagesWindowTopComponent")
+@ActionReference(path = "Menu/Window" /*, position = 333 */)
+@TopComponent.OpenActionRegistration(displayName = "#CTL_RemoteUsagesWindowAction",
+preferredID = "RemoteUsagesWindowTopComponent")
+public final class RemoteUsagesWindowTopComponent extends TopComponent implements ExplorerManager.Provider {
+
+    public RemoteUsagesWindowTopComponent() {
+        initComponents();
+        setName(NbBundle.getMessage(RemoteUsagesWindowTopComponent.class, "CTL_RemoteUsagesWindowTopComponent"));
+        setToolTipText(NbBundle.getMessage(RemoteUsagesWindowTopComponent.class, "HINT_RemoteUsagesWindowTopComponent"));
+
+        BeanTreeView btv = new BeanTreeView();
+
+        add(btv, BorderLayout.CENTER);
+
+        btv.setRootVisible(false);
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        setLayout(new java.awt.BorderLayout());
+    }// </editor-fold>//GEN-END:initComponents
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    // End of variables declaration//GEN-END:variables
+    @Override
+    public void componentOpened() {
+        // TODO add custom code on component opening
+    }
+
+    @Override
+    public void componentClosed() {
+        // TODO add custom code on component closing
+    }
+
+    void writeProperties(java.util.Properties p) {
+        // better to version settings since initial version as advocated at
+        // http://wiki.apidesign.org/wiki/PropertyFiles
+        p.setProperty("version", "1.0");
+        // TODO store your settings
+    }
+
+    void readProperties(java.util.Properties p) {
+        String version = p.getProperty("version");
+        // TODO read your settings according to their version
+    }
+
+    @Override
+    public int getPersistenceType() {
+        return PERSISTENCE_NEVER;
+    }
+
+    private final ExplorerManager manager = new ExplorerManager();
+
+    @Override
+    public ExplorerManager getExplorerManager() {
+        return manager;
+    }
+
+    @Override
+    public void open() {
+        Mode outputMode = WindowManager.getDefault().findMode("output");
+
+        if (outputMode != null)
+            outputMode.dockInto(this);
+        super.open();
+        requestActive();
+        requestFocusInWindow(false);
+    }
+
+    public static void openFor(Node n) {
+        RemoteUsagesWindowTopComponent c = new RemoteUsagesWindowTopComponent();
+
+        c.manager.setRootContext(n);
+        c.open();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/hints/RemotelyUnused.java
----------------------------------------------------------------------
diff --git a/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/hints/RemotelyUnused.java b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/hints/RemotelyUnused.java
new file mode 100644
index 0000000..72302d5
--- /dev/null
+++ b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/hints/RemotelyUnused.java
@@ -0,0 +1,152 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.ide.usages.hints;
+
+import com.sun.source.tree.Tree.Kind;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.BitSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.TermQuery;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.modules.jackpot30.common.api.JavaUtils;
+import org.netbeans.modules.jackpot30.common.api.LuceneHelpers.BitSetCollector;
+import org.netbeans.modules.jackpot30.remoting.api.LocalCache;
+import org.netbeans.modules.jackpot30.remoting.api.LocalCache.Task;
+import org.netbeans.modules.jackpot30.remoting.api.RemoteIndex;
+import org.netbeans.modules.jackpot30.remoting.api.WebUtilities;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
+import org.netbeans.spi.java.hints.Hint;
+import org.netbeans.spi.java.hints.HintContext;
+import org.netbeans.spi.java.hints.TriggerTreeKind;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ *
+ * @author lahvac
+ */
+@Hint(displayName="#DN_RemotelyUnused", description="#DESC_RemotelyUnused", category="general", enabled=false)
+@Messages({"DN_RemotelyUnused=Unused in Remote Projects",
+           "DESC_RemotelyUnused=Not used in any known remote project",
+           "ERR_NoUsages=No usages found in any know projects"})
+public class RemotelyUnused {
+
+    private static final String VAL_UNUSED = "unused";
+    private static final String VAL_USED = "used";
+    private static final String VAL_UNKNOWN = "unknown";
+
+    @TriggerTreeKind({Kind.VARIABLE, Kind.METHOD})
+    public static ErrorDescription hint(HintContext ctx) throws URISyntaxException, IOException {
+        Element toSearch = ctx.getInfo().getTrees().getElement(ctx.getPath());
+
+        if (toSearch == null) return null;
+        if (!toSearch.getKind().isField() && toSearch.getKind() != ElementKind.METHOD && toSearch.getKind() != ElementKind.CONSTRUCTOR) return null;
+        if (toSearch.getKind() == ElementKind.METHOD && ctx.getInfo().getElementUtilities().overridesMethod((ExecutableElement) toSearch)) return null;
+
+        final String serialized = JavaUtils.serialize(ElementHandle.create(toSearch));
+
+        for (RemoteIndex idx : RemoteIndex.loadIndices()) {
+            String result = LocalCache.runOverLocalCache(idx, new Task<IndexReader, String>() {
+                @Override
+                public String run(IndexReader reader, AtomicBoolean cancel) throws IOException {
+                    Query query = new TermQuery(new Term("usagesSignature", serialized));
+                    Searcher s = new IndexSearcher(reader);
+                    BitSet matchingDocuments = new BitSet(reader.maxDoc());
+                    Collector c = new BitSetCollector(matchingDocuments);
+
+                    s.search(query, c);
+
+                    for (int docNum = matchingDocuments.nextSetBit(0); docNum >= 0; docNum = matchingDocuments.nextSetBit(docNum + 1)) {
+                        if (cancel.get()) return VAL_UNKNOWN;
+
+                        final Document doc = reader.document(docNum);
+
+                        return doc.get("usagesUsages");
+                    }
+
+                    return VAL_UNKNOWN;
+                }
+            }, null, new AtomicBoolean()/*XXX*/);
+
+            if (result == null) {
+                URI resolved = new URI(idx.remote.toExternalForm() + "/usages/search?path=" + WebUtilities.escapeForQuery(idx.remoteSegment) + "&signatures=" + WebUtilities.escapeForQuery(serialized));
+                String response = WebUtilities.requestStringResponse(resolved, new AtomicBoolean());
+
+                if (response != null) {
+                    result = response.trim().isEmpty() ? VAL_UNUSED : VAL_USED;
+                } else {
+                    result = VAL_UNKNOWN;
+                }
+                final String resultFin = result;
+                LocalCache.saveToLocalCache(idx, new Task<IndexWriter, Void>() {
+                    @Override public Void run(IndexWriter p, AtomicBoolean cancel) throws IOException {
+                        Document doc = new Document();
+                        doc.add(new Field("usagesSignature", serialized, Store.NO, Index.NOT_ANALYZED));
+                        doc.add(new Field("usagesUsages", resultFin, Store.YES, Index.NO));
+                        p.addDocument(doc);
+                        return null;
+                    }
+                });
+            }
+            if (!VAL_UNUSED.equals(result)) return null;
+        }
+
+        return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_NoUsages());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/ide/usages/test/unit/src/org/netbeans/modules/jackpot30/ide/usages/NodesTest.java
----------------------------------------------------------------------
diff --git a/remoting/ide/usages/test/unit/src/org/netbeans/modules/jackpot30/ide/usages/NodesTest.java b/remoting/ide/usages/test/unit/src/org/netbeans/modules/jackpot30/ide/usages/NodesTest.java
new file mode 100644
index 0000000..696378e
--- /dev/null
+++ b/remoting/ide/usages/test/unit/src/org/netbeans/modules/jackpot30/ide/usages/NodesTest.java
@@ -0,0 +1,158 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.jackpot30.ide.usages;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import javax.lang.model.element.TypeElement;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.SourceUtilsTestUtil;
+import org.netbeans.api.java.source.TestUtilities;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.nodes.Node;
+
+/**
+ *
+ * @author lahvac
+ */
+public class NodesTest extends NbTestCase {
+
+    public NodesTest(String testName) {
+        super(testName);
+    }
+
+    public void testA() throws Exception {
+        FileObject data = FileUtil.createData(src, "test/Test.java");
+        TestUtilities.copyStringToFile(data, "package test;\n" +
+                                             "public class Test {\n" +
+                                             "    public Test () {}\n" +
+                                             "    private void test() { new Test(); }\n" +
+                                             "}\n");
+        CompilationInfo info = SourceUtilsTestUtil.getCompilationInfo(JavaSource.forFileObject(data), Phase.RESOLVED);
+        ElementHandle<?> eh = ElementHandle.create(info.getTopLevelElements().get(0).getEnclosedElements().get(0));
+        List<Node> constructed = new ArrayList<Node>();
+
+        Nodes.computeOccurrences(data, eh, EnumSet.of(RemoteUsages.SearchOptions.USAGES), constructed);
+
+        Node n = constructed.get(0);
+
+        assertEquals("    private void test() { new <b>Test</b>(); }<br>", n.getHtmlDisplayName());
+    }
+
+    public void testMethodImplementations() throws Exception {
+        FileObject data = FileUtil.createData(src, "test/Test.java");
+        TestUtilities.copyStringToFile(data, "package test;\n" +
+                                             "public class Test implements Runnable {\n" +
+                                             "    public void run() {\n" +
+                                             "         Runnable r = null;\n" +
+                                             "         r.run();\n" +
+                                             "    }\n" +
+                                             "}\n");
+        CompilationInfo info = SourceUtilsTestUtil.getCompilationInfo(JavaSource.forFileObject(data), Phase.RESOLVED);
+        TypeElement runnable = info.getElements().getTypeElement("java.lang.Runnable");
+
+        assertNotNull(runnable);
+
+        ElementHandle<?> eh = ElementHandle.create(runnable.getEnclosedElements().get(0));
+        List<Node> constructed = new ArrayList<Node>();
+
+        Nodes.computeOccurrences(data, eh, EnumSet.of(RemoteUsages.SearchOptions.SUB), constructed);
+
+        assertEquals(1, constructed.size());
+        
+        Node n = constructed.get(0);
+
+        assertEquals("    public void <b>run</b>() {<br>", n.getHtmlDisplayName());
+    }
+
+    public void testSubtypes() throws Exception {
+        FileObject data = FileUtil.createData(src, "test/Test.java");
+        TestUtilities.copyStringToFile(data, "package test;\n" +
+                                             "public class Test implements Runnable {\n" +
+                                             "    public void run() {\n" +
+                                             "         Runnable r = null;\n" +
+                                             "         r.run();\n" +
+                                             "    }\n" +
+                                             "}\n");
+        CompilationInfo info = SourceUtilsTestUtil.getCompilationInfo(JavaSource.forFileObject(data), Phase.RESOLVED);
+        TypeElement runnable = info.getElements().getTypeElement("java.lang.Runnable");
+
+        assertNotNull(runnable);
+
+        ElementHandle<?> eh = ElementHandle.create(runnable);
+        List<Node> constructed = new ArrayList<Node>();
+
+        Nodes.computeOccurrences(data, eh, EnumSet.of(RemoteUsages.SearchOptions.SUB), constructed);
+
+        assertEquals(1, constructed.size());
+
+        Node n = constructed.get(0);
+
+        assertEquals("public class <b>Test</b> implements Runnable {<br>", n.getHtmlDisplayName());
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        SourceUtilsTestUtil.prepareTest(new String[0], new Object[0]);
+        prepareTest();
+        super.setUp();
+    }
+
+    private FileObject src;
+
+    private void prepareTest() throws Exception {
+        FileObject workdir = SourceUtilsTestUtil.makeScratchDir(this);
+
+        src = FileUtil.createFolder(workdir, "src");
+
+        FileObject cache = FileUtil.createFolder(workdir, "cache");
+
+        CacheFolder.setCacheFolder(cache);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/post-build.sh
----------------------------------------------------------------------
diff --git a/remoting/post-build.sh b/remoting/post-build.sh
new file mode 100755
index 0000000..1d1dc51
--- /dev/null
+++ b/remoting/post-build.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+#
+# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+# Other names may be trademarks of their respective owners.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# Contributor(s):
+#
+# The Original Software is NetBeans. The Initial Developer of the Original
+# Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+# Microsystems, Inc. All Rights Reserved.
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+
+cp -r ../duplicates/server/indexer/build/cluster/* build/indexing-backend/indexer/indexer
+cp -r ../duplicates/ide/build/cluster/* build/indexing-backend/indexer/indexer
+cp -r ../remoting/ide/build/cluster/* build/indexing-backend/indexer/indexer
+cp -r ../language/ide/build/cluster/* build/indexing-backend/indexer/indexer
+cp -r ../duplicates/server/web/*/dist/*.jar build/indexing-backend/web/lib
+cp -r ../language/server/web/*/dist/*.jar build/indexing-backend/web/lib
+cp -r ../language/server/web/*/dist/lib/*.jar build/indexing-backend/web/lib
+
+(cd build; zip -r indexing-backend-feature-packed.zip indexing-backend) || exit 1
+(cd build; zip -r indexing-backend-feature-packed-shortened.zip `find indexing-backend -type f | grep -v indexing-backend/indexer/enterprise/ | grep -v indexing-backend/indexer/apisupport/  | grep -v indexing-backend/indexer/cnd/   | grep -v indexing-backend/indexer/dlight/   | grep -v indexing-backend/indexer/harness/   | grep -v indexing-backend/indexer/ide/   | grep -v indexing-backend/indexer/java   | grep -v indexing-backend/indexer/nb/   | grep -v indexing-backend/indexer/platform/   | grep -v indexing-backend/indexer/profiler/   | grep -v indexing-backend/indexer/websvccommon/`) || exit 1
+
+(cd server/tests; ./run-integration-tests) || exit 1

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/server/hudson/pom.xml
----------------------------------------------------------------------
diff --git a/remoting/server/hudson/pom.xml b/remoting/server/hudson/pom.xml
new file mode 100644
index 0000000..27062be
--- /dev/null
+++ b/remoting/server/hudson/pom.xml
@@ -0,0 +1,77 @@
+<!--
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+
+Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+Other names may be trademarks of their respective owners.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 2 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://www.netbeans.org/cddl-gplv2.html
+or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+specific language governing permissions and limitations under the
+License.  When distributing the software, include this License Header
+Notice in each file and include the License file at
+nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the GPL Version 2 section of the License file that
+accompanied this code. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+Contributor(s):
+
+The Original Software is NetBeans. The Initial Developer of the Original
+Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+Microsystems, Inc. All Rights Reserved.
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 2, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 2] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 2 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 2 code and therefore, elected the GPL
+Version 2 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <name>Jackpot 3.0 Hudson Plugin</name>
+  <parent>
+    <groupId>org.jenkins-ci.plugins</groupId>
+    <artifactId>plugin</artifactId>
+    <version>1.424</version><!-- which version of Jenkins is this plugin built against? -->
+  </parent>
+
+  <groupId>org.netbeans.modules.jackpot30.hudson</groupId>
+  <artifactId>org.netbeans.modules.jackpot30.hudson</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>hpi</packaging>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <!-- get every artifact through repo.jenkins-ci.org, which proxies all the artifacts that we need -->
+  <repositories>
+    <repository>
+      <id>repo.jenkins-ci.org</id>
+      <url>http://repo.jenkins-ci.org/public/</url>
+    </repository>
+  </repositories>
+
+  <pluginRepositories>
+    <pluginRepository>
+      <id>repo.jenkins-ci.org</id>
+      <url>http://repo.jenkins-ci.org/public/</url>
+    </pluginRepository>
+  </pluginRepositories>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/ClearIndexProperty.java
----------------------------------------------------------------------
diff --git a/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/ClearIndexProperty.java b/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/ClearIndexProperty.java
new file mode 100644
index 0000000..cc30684
--- /dev/null
+++ b/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/ClearIndexProperty.java
@@ -0,0 +1,131 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.hudson;
+
+import hudson.Extension;
+import hudson.model.Action;
+import hudson.model.Hudson;
+import hudson.model.Job;
+import hudson.model.JobProperty;
+import hudson.model.JobPropertyDescriptor;
+import hudson.model.Project;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import javax.servlet.ServletException;
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.netbeans.modules.jackpot30.hudson.IndexingBuilder.DescriptorImpl;
+
+/**
+ *
+ * @author lahvac
+ */
+public class ClearIndexProperty extends JobProperty<Job<?, ?>> {
+    
+    @Override
+    public Collection<? extends Action> getJobActions(Job<?, ?> job) {
+        return Collections.singleton(new ClearIndexAction(job));
+    }
+
+    public static final class ClearIndexAction implements Action {
+
+        private final Job<?, ?> job;
+
+        public ClearIndexAction(Job<?, ?> job) {
+            this.job = job;
+        }
+
+        public String getIconFileName() {
+            return "folder-delete.gif";
+        }
+
+        public String getDisplayName() {
+            return "Clear Job Index";
+        }
+
+        public String getUrlName() {
+            return "clearIndex";
+        }
+
+        public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException {
+            File cacheDir = ((DescriptorImpl) DescriptorImpl.find(DescriptorImpl.class.getName())).getCacheDir();
+            File jobCacheDir = new File(cacheDir, IndexingBuilder.codeNameForJob(job));
+
+            deleteRecursivelly(jobCacheDir);
+            
+            rsp.forwardToPreviousPage(req);
+        }
+
+        private static void deleteRecursivelly(File f) {
+            File[] files = f.listFiles();
+
+            if (files != null) {
+                for (File c : files) {
+                    deleteRecursivelly(c);
+                }
+            }
+
+            f.delete();
+        }
+    }
+
+    @Extension // this marker indicates Hudson that this is an implementation of an extension point.
+    public static final class ClearIndexPropertyDescription extends JobPropertyDescriptor  {
+
+        @Override
+        public JobProperty<?> newInstance(StaplerRequest req, JSONObject formData) throws FormException {
+            return new ClearIndexProperty();
+        }
+
+        @Override
+        public String getDisplayName() {
+            return "Clear Indices Desc";
+        }
+
+        @Override
+        public boolean isApplicable(Class<? extends Job> jobType) {
+            return Project.class.isAssignableFrom(jobType);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexGlobalAction.java
----------------------------------------------------------------------
diff --git a/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexGlobalAction.java b/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexGlobalAction.java
new file mode 100644
index 0000000..912fb37
--- /dev/null
+++ b/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexGlobalAction.java
@@ -0,0 +1,105 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011-2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011-2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.jackpot30.hudson;
+
+import hudson.Extension;
+import hudson.model.RootAction;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLConnection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.servlet.ServletException;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+
+/**
+ *
+ * @author lahvac
+ */
+@Extension
+public class IndexGlobalAction implements RootAction {
+
+    public String getIconFileName() {
+        return null;
+    }
+
+    public String getDisplayName() {
+        return "Jackpot 3.0 Index";
+    }
+
+    public String getUrlName() {
+        return "index";
+    }
+
+    public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, InterruptedException {
+        InputStream in = null;
+        OutputStream out = null;
+        try {
+            URI delegateURI = new URI("http://localhost:9998/index" + req.getRestOfPath() + "?" + req.getQueryString());
+            URLConnection inConnection = delegateURI.toURL().openConnection();
+            in = inConnection.getInputStream();
+
+            rsp.setContentType(inConnection.getContentType());
+            rsp.setCharacterEncoding(inConnection.getContentEncoding());
+
+            out = rsp.getOutputStream();
+            int read;
+
+            while ((read = in.read()) != (-1)) {
+                out.write(read);
+            }
+        } catch (URISyntaxException ex) {
+            Logger.getLogger(IndexGlobalAction.class.getName()).log(Level.SEVERE, null, ex);
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexingBuilder.java
----------------------------------------------------------------------
diff --git a/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexingBuilder.java b/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexingBuilder.java
new file mode 100644
index 0000000..8e933e8
--- /dev/null
+++ b/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexingBuilder.java
@@ -0,0 +1,384 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.hudson;
+
+import hudson.Extension;
+import hudson.FilePath;
+import hudson.FilePath.FileCallable;
+import hudson.Launcher;
+import hudson.Proc;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.BuildListener;
+import hudson.model.Descriptor;
+import hudson.model.Descriptor.FormException;
+import hudson.model.Hudson;
+import hudson.model.Job;
+import hudson.remoting.VirtualChannel;
+import hudson.tasks.Builder;
+import hudson.util.ArgumentListBuilder;
+import hudson.util.FormValidation;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Serializable;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.AncestorInPath;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.StaplerRequest;
+
+/**
+ *
+ * @author lahvac
+ */
+public class IndexingBuilder extends Builder {
+
+    private final String projectName;
+    private final String toolName;
+    private final String indexSubDirectory;
+    private final String ignorePatterns;
+    
+    public IndexingBuilder(StaplerRequest req, JSONObject json) throws FormException {
+        projectName = json.getString("projectName");
+        toolName = json.optString("toolName", IndexingTool.DEFAULT_INDEXING_NAME);
+        indexSubDirectory = json.optString("indexSubDirectory", "");
+        ignorePatterns = json.optString("ignorePatterns", "");
+    }
+
+    @DataBoundConstructor
+    public IndexingBuilder(String projectName, String toolName, String indexSubDirectory, String ignorePatterns) {
+        this.projectName = projectName;
+        this.toolName = toolName;
+        this.indexSubDirectory = indexSubDirectory;
+        this.ignorePatterns = ignorePatterns != null ? ignorePatterns : "";
+    }
+
+    public String getProjectName() {
+        return projectName;
+    }
+
+    public String getToolName() {
+        return toolName;
+    }
+
+    public String getIndexSubDirectory() {
+        return indexSubDirectory;
+    }
+
+    public String getIgnorePatterns() {
+        return ignorePatterns;
+    }
+
+    @Override
+    public DescriptorImpl getDescriptor() {
+        return (DescriptorImpl) super.getDescriptor();
+    }
+
+    @Override
+    public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
+        IndexingTool t = findSelectedTool();
+
+        if (t == null) {
+            listener.getLogger().println("Cannot find indexing tool: " + toolName);
+            return false;
+        }
+
+        t = t.forNode(build.getBuiltOn(), listener);
+
+        listener.getLogger().println("Looking for projects in: " + build.getWorkspace().getRemote());
+
+        FilePath base = indexSubDirectory == null || indexSubDirectory.isEmpty() ? build.getWorkspace() : build.getWorkspace().child(indexSubDirectory); //XXX: child also supports absolute paths! absolute paths should not be allowed here (for security)
+        RemoteResult res = base.act(new FindProjects(t.getHome(), getIgnorePatterns()));
+
+        listener.getLogger().println("Running: " + toolName + " on projects: " + res);
+
+        String codeName = codeNameForJob(build.getParent());
+        ArgumentListBuilder args = new ArgumentListBuilder();
+        FilePath targetZip = build.getBuiltOn().getRootPath().createTempFile(codeName, "zip");
+
+        //XXX: there should be a way to specify Java runtime!
+        args.add(new File(t.getHome(), "index.sh")); //XXX
+        args.add(codeName);
+        args.add(projectName); //XXX
+        args.add(targetZip);
+        args.add(res.root);
+        args.add(res.foundProjects.toArray(new String[0]));
+
+        Proc indexer = launcher.launch().pwd(base)
+                                        .cmds(args)
+                                        .envs("JPT30_INFO=BUILD_ID=" + build.getNumber())
+                                        .stdout(listener)
+                                        .start();
+
+        indexer.join();
+
+        InputStream ins = targetZip.read();
+
+        try {
+            UploadIndex.uploadIndex(codeName, ins);
+        } finally {
+            ins.close();
+            targetZip.delete();
+        }
+
+        return true;
+    }
+
+    public IndexingTool findSelectedTool() {
+        for (IndexingTool t : getDescriptor().getIndexingTools()) {
+            if (toolName.equals(t.getName())) return t;
+        }
+
+        return null;
+    }
+
+    private static void findProjects(File root, Collection<String> result, Iterable<Pattern> markers, Iterable<Pattern> ignores, Pattern perProjectIgnore, StringBuilder relPath) {
+        int len = relPath.length();
+        boolean first = relPath.length() == 0;
+
+        for (Pattern marker : markers) {
+            Matcher m = marker.matcher(relPath);
+
+            if (m.matches()) {
+                if (perProjectIgnore == null || !perProjectIgnore.matcher(relPath).matches()) {
+                    result.add(m.group(1));
+                }
+                break;
+            }
+        }
+
+        File[] children = root.listFiles();
+
+        if (children != null) {
+            Arrays.sort(children, new Comparator<File>() {
+                public int compare(File o1, File o2) {
+                    return o1.getName().compareTo(o2.getName());
+                }
+            });
+            OUTER: for (File c : children) {
+                for (Pattern ignore : ignores) {
+                    if (ignore.matcher(c.getName()).matches()) continue OUTER;
+                }
+                if (!first)
+                    relPath.append("/");
+                relPath.append(c.getName());
+                findProjects(c, result, markers, ignores, perProjectIgnore, relPath);
+                relPath.delete(len, relPath.length());
+            }
+        }
+    }
+
+    public static String codeNameForJob(Job<?, ?> job) {
+        return job.getName();
+    }
+
+    private static final class RemoteResult implements Serializable {
+        private final Collection<String> foundProjects;
+        private final String root;
+        public RemoteResult(Collection<String> foundProjects, String root) {
+            this.foundProjects = foundProjects;
+            this.root = root;
+        }
+    }
+    
+    @Extension // this marker indicates Hudson that this is an implementation of an extension point.
+    public static class DescriptorImpl extends Descriptor<Builder> { //non-final for tests
+
+        private File cacheDir;
+        private String webVMOptions;
+
+        public DescriptorImpl() {
+            cacheDir = new File(Hudson.getInstance().getRootDir(), "index").getAbsoluteFile();
+            webVMOptions = "";
+            load();
+        }
+
+        public File getCacheDir() {
+            return cacheDir;
+        }
+
+        public String getWebVMOptions() {
+            return webVMOptions;
+        }
+
+        @Override
+        public Builder newInstance(StaplerRequest req, JSONObject formData) throws FormException {
+            return new IndexingBuilder(req, formData);
+        }
+
+        @Override
+        public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
+            cacheDir = new File(json.getString("cacheDir"));
+
+            String newWebVMOptions = json.getString("webVMOptions");
+
+            if (newWebVMOptions == null) newWebVMOptions = "";
+
+            boolean restartWebFrontEnd = !webVMOptions.equals(newWebVMOptions);
+
+            webVMOptions = newWebVMOptions;
+            
+            save();
+            
+            boolean result = super.configure(req, json);
+
+            if (restartWebFrontEnd)
+                WebFrontEnd.restart();
+            
+            return result;
+        }
+
+        @Override
+        public String getDisplayName() {
+            return "Run Indexers";
+        }
+
+        public List<? extends IndexingTool> getIndexingTools() {
+            return Arrays.asList(Hudson.getInstance().getDescriptorByType(IndexingTool.DescriptorImpl.class).getInstallations());
+        }
+
+        public boolean hasNonStandardIndexingTool() {
+            return getIndexingTools().size() > 1;
+        }
+
+        public FormValidation doCheckIndexSubDirectory(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException, InterruptedException {
+            FilePath workspace = project.getSomeWorkspace();
+            if (workspace == null || !workspace.exists() || value == null || value.isEmpty() || workspace.child(value).isDirectory()) {
+                return FormValidation.ok();
+            } else {
+                return workspace.validateRelativeDirectory(value);
+            }
+        }
+
+        public FormValidation doCheckIgnorePatterns(@AncestorInPath AbstractProject project, @QueryParameter String ignorePatterns) throws IOException, InterruptedException {
+            FilePath workspace = project.getSomeWorkspace();
+            if (workspace == null || !workspace.exists() || ignorePatterns == null || ignorePatterns.isEmpty()) {
+                return FormValidation.ok();
+            } else {
+                try {
+                    Pattern.compile(ignorePatterns);
+                    return FormValidation.ok();
+                } catch (PatternSyntaxException ex) {
+                    return FormValidation.error("Not a valid regular expression (" + ex.getDescription() + ")");
+                }
+            }
+        }
+
+    }
+
+    private static class FindProjects implements FileCallable<RemoteResult> {
+        private final String toolHome;
+        private final String perProjectIgnore;
+        public FindProjects(String toolHome, String perProjectIgnore) {
+            this.toolHome = toolHome;
+            this.perProjectIgnore = perProjectIgnore;
+        }
+        public RemoteResult invoke(File file, VirtualChannel vc) throws IOException, InterruptedException {
+            List<Pattern> projectMarkers = new ArrayList<Pattern>();
+            List<Pattern> ignorePatterns = new ArrayList<Pattern>();
+            FilePath indexerPath = new FilePath(new File(toolHome, "indexer"));
+            for (FilePath clusters : indexerPath.listDirectories()) {
+                FilePath patternsDirectory = clusters.child("patterns");
+                if (!patternsDirectory.isDirectory()) continue;
+                for (FilePath patterns : patternsDirectory.list()) {
+                    if (patterns.getName().startsWith("project-marker-")) {
+                        projectMarkers.addAll(readPatterns(patterns));
+                    } else if (patterns.getName().startsWith("ignore-")) {
+                        ignorePatterns.addAll(readPatterns(patterns));
+                    }
+                }
+            }
+            
+            Set<String> projects = new HashSet<String>();
+
+            findProjects(file, projects, projectMarkers, ignorePatterns, perProjectIgnore == null || perProjectIgnore.trim().isEmpty() ? null : Pattern.compile(perProjectIgnore), new StringBuilder());
+
+            return new RemoteResult(projects, file.getCanonicalPath()/*XXX: will resolve symlinks!!!*/);
+        }
+    }
+
+    private static List<Pattern> readPatterns(FilePath source) {
+        BufferedReader in = null;
+
+        List<Pattern> result = new ArrayList<Pattern>();
+        try {
+            in = new BufferedReader(new InputStreamReader(source.read(), "UTF-8"));
+            String line;
+
+            while ((line = in.readLine()) != null) {
+                line = line.trim();
+                if (!line.isEmpty()) {
+                    result.add(Pattern.compile(line));
+                }
+            }
+        } catch (IOException ex) {
+            Logger.getLogger(IndexingBuilder.class.getName()).log(Level.SEVERE, null, ex);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException ex) {
+                    Logger.getLogger(IndexingBuilder.class.getName()).log(Level.SEVERE, null, ex);
+                }
+            }
+        }
+
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexingTool.java
----------------------------------------------------------------------
diff --git a/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexingTool.java b/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexingTool.java
new file mode 100644
index 0000000..3fae28f
--- /dev/null
+++ b/remoting/server/hudson/src/main/java/org/netbeans/modules/jackpot30/hudson/IndexingTool.java
@@ -0,0 +1,100 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.jackpot30.hudson;
+
+import hudson.Extension;
+import hudson.model.Node;
+import hudson.model.TaskListener;
+import hudson.slaves.NodeSpecific;
+import hudson.tools.ToolDescriptor;
+import hudson.tools.ToolInstallation;
+import hudson.tools.ToolProperty;
+import java.io.IOException;
+import java.util.List;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+/**
+ *
+ * @author lahvac
+ */
+public class IndexingTool extends ToolInstallation implements NodeSpecific<IndexingTool> {
+
+    public static final String DEFAULT_INDEXING_NAME = "Main NetBeans Indexing";
+    
+    @DataBoundConstructor
+    public IndexingTool(String name, String home, List<? extends ToolProperty<?>> properties) {
+        super(name, home, properties);
+    }
+
+    public IndexingTool forNode(Node node, TaskListener tl) throws IOException, InterruptedException {
+        return new IndexingTool(getName(), translateFor(node, tl), getProperties().toList());
+    }
+
+    @Extension
+    public static final class DescriptorImpl extends ToolDescriptor<IndexingTool> {
+
+        @Override
+        public String getDisplayName() {
+            return "Indexing Tool";
+        }
+
+        private IndexingTool[] installations;
+
+        @Override
+        public IndexingTool[] getInstallations() {
+            if (installations == null) {
+                load();
+
+                if (installations == null) {
+                    installations = new IndexingTool[0];
+                }
+            }
+
+            return installations.clone();
+        }
+
+        @Override
+        public void setInstallations(IndexingTool... installations) {
+            this.installations = installations.clone();
+            save();
+        }
+
+
+    }
+}