blob: ea9bcfbf62f1cfd07cc2ccbae9642029cdfebeb5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.java.j2sedeploy;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import org.apache.tools.ant.module.api.support.ActionUtils;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.scripting.Scripting;
import org.netbeans.modules.java.j2seproject.api.J2SEPropertyEvaluator;
import org.netbeans.spi.project.ActionProgress;
import org.netbeans.spi.project.ActionProvider;
import org.netbeans.spi.project.LookupProvider;
import org.netbeans.spi.project.ProjectServiceProvider;
import org.netbeans.spi.project.support.ant.GeneratedFilesHelper;
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.execution.ExecutorTask;
import org.openide.filesystems.FileObject;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.Parameters;
import org.openide.util.RequestProcessor;
import org.openide.util.Task;
import org.openide.util.TaskListener;
/**
*
* @author Tomas Zezula
* @author Petr Somol
*/
@ProjectServiceProvider(
service = ActionProvider.class,
projectTypes={@LookupProvider.Registration.ProjectType(id="org-netbeans-modules-java-j2seproject",position=500)})
public class J2SEDeployActionProvider implements ActionProvider {
private static final String TARGET_BUILD_NATIVE = "build-native"; //NOI18N
private static final String NOSCRIPT_SUFFIX = "-noscript"; //NOI18N
private static final String PROP_PACKAGE_TYPE = "native.bundling.type"; //NOI18N
private static final String PROP_DISABLE_JAR_CREATION = "jar.archive.disabled";//NOI18N
private static final String PROP_BUILD_FILE = "buildfile"; //NOI18N
private static final RequestProcessor RP = new RequestProcessor(J2SEDeployActionProvider.class);
private final Listener listener;
private boolean isJSAvailable = true;
private boolean isJSAvailableChecked = false;
public J2SEDeployActionProvider(@NonNull final Project prj) {
this.listener = new Listener(prj);
}
@Override
public String[] getSupportedActions() {
final Set<NativeBundleType> nbts = NativeBundleType.getSupported();
final Queue<String> res = new ArrayDeque<>(nbts.size());
for (NativeBundleType nbt : nbts) {
res.add(nbt.getCommand());
}
return res.toArray(new String[res.size()]);
}
@NbBundle.Messages("LBL_No_Build_XML_Found=The project does not have a valid build script {0}.")
@Override
public void invokeAction(String command, Lookup context) throws IllegalArgumentException {
final Project prj = context.lookup(Project.class);
if (prj == null) {
throw new IllegalArgumentException(String.format(
"The context %s has no Project.", //NOI18N
context));
}
final NativeBundleType nbt = NativeBundleType.forCommand(command);
if (nbt == null) {
throw new IllegalArgumentException(String.format(
"Unsupported command %s.", //NOI18N
command));
}
final FileObject buildScript = findBuildScript(listener.getProject());
if (buildScript == null || !buildScript.isValid()) {
NotifyDescriptor nd = new NotifyDescriptor.Message(
Bundle.LBL_No_Build_XML_Found(getBuildXmlName(listener.getProject())),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(nd);
return;
}
final ActionProgress listener = ActionProgress.start(context);
boolean success = false;
try {
final Properties p = new Properties();
p.setProperty(PROP_PACKAGE_TYPE, nbt.getAntProperyValue());
p.setProperty(PROP_DISABLE_JAR_CREATION, Boolean.TRUE.toString());
String noScript = isJavaScriptAvailable() ? "" : NOSCRIPT_SUFFIX; // NOI18N
final ExecutorTask task = ActionUtils.runTarget(
buildScript,
new String[] {TARGET_BUILD_NATIVE.concat(noScript)},
p);
task.addTaskListener(new TaskListener() {
@Override
public void taskFinished(Task _tmp) {
listener.finished(task.result() == 0);
}
});
success = true;
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} finally {
if (!success) {
listener.finished(false);
}
}
}
@Override
public boolean isActionEnabled(String command, Lookup context) throws IllegalArgumentException {
return supportsCommand(command) &&
listener.getProject().equals(context.lookup(Project.class)) &&
listener.isEnabled();
}
private boolean isJavaScriptAvailable() {
if(isJSAvailableChecked) {
return isJSAvailable;
}
ScriptEngineManager mgr = Scripting.createManager();
List<ScriptEngineFactory> factories = mgr.getEngineFactories();
for (ScriptEngineFactory factory: factories) {
List<String> engNames = factory.getNames();
for(String name: engNames) {
if(name.equalsIgnoreCase("js") || name.equalsIgnoreCase("javascript")) { //NOI18N
isJSAvailableChecked = true;
isJSAvailable = true;
return isJSAvailable;
}
}
}
isJSAvailableChecked = true;
isJSAvailable = false;
return isJSAvailable;
}
private boolean supportsCommand (@NonNull final String command) {
for (String supportedCommand : getSupportedActions()) {
if (supportedCommand.equals(command)) {
return true;
}
}
return false;
}
@CheckForNull
private static FileObject findBuildScript (@NonNull final Project prj) {
return prj.getProjectDirectory().getFileObject(getBuildXmlName(prj));
}
@NonNull
private static String getBuildXmlName (@NonNull final Project prj) {
final J2SEPropertyEvaluator evalProvider = prj.getLookup().lookup(J2SEPropertyEvaluator.class);
String buildScriptPath = evalProvider == null ?
null :
evalProvider.evaluator().getProperty(PROP_BUILD_FILE);
if (buildScriptPath == null) {
buildScriptPath = GeneratedFilesHelper.BUILD_XML_PATH;
}
return buildScriptPath;
}
private static final class Listener implements Runnable, PropertyChangeListener {
private final Project prj;
private final RequestProcessor.Task refresh;
private final AtomicBoolean initialized;
private volatile Boolean cachedEnabled;
Listener(@NonNull final Project prj) {
Parameters.notNull("prj", prj); //NOI18N
this.prj = prj;
this.initialized = new AtomicBoolean();
refresh = RP.create(this);
}
@Override
public void run() {
ProjectManager.mutex().readAccess(new Runnable() {
@Override
public void run() {
isEnabled();
}
});
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
final String propName = evt.getPropertyName();
if (propName == null || J2SEDeployProperties.NATIVE_BUNDLING_ENABLED.equals(propName)) {
cachedEnabled = null;
refresh.schedule(0);
}
}
boolean isEnabled() {
Boolean res = cachedEnabled;
if (res != null) {
return res;
}
final J2SEPropertyEvaluator j2seEval = prj.getLookup().lookup(J2SEPropertyEvaluator.class);
if (j2seEval == null) {
cachedEnabled = res = Boolean.FALSE;
} else {
final PropertyEvaluator eval = j2seEval.evaluator();
if (initialized.compareAndSet(false, true)) {
eval.addPropertyChangeListener(this);
}
cachedEnabled = res = isTrue(eval.getProperty(J2SEDeployProperties.NATIVE_BUNDLING_ENABLED));
}
return res;
}
@NonNull
Project getProject() {
return prj;
}
private static boolean isTrue(@NullAllowed String value) {
return "true".equals(value) || //NOI18N
"yes".equals(value) || //NOI18N
"on".equals(value); //NOI18N
}
}
}