// Path: src/main/java/net/imagej/server/WebCommandInfo.java
// public class WebCommandInfo extends CommandInfo {
// 
// 	private final Module relatedModule;
// 
// 	public WebCommandInfo(CommandInfo commandInfo, Module module) {
// 		super(commandInfo);
// 		relatedModule = module;
// 	}
// 
// 	@Override
// 	public Iterable<ModuleItem<?>> inputs() {
// 
// 		final List<WebCommandModuleItem<?>> checkedInputs = new ArrayList<>();
// 		for (final ModuleItem<?> input : super.inputs()) {
// 			if (input instanceof CommandModuleItem) {
// 				final WebCommandModuleItem<Object> webCommandModuleItem =
// 					new WebCommandModuleItem<>(this, ((CommandModuleItem<?>) input)
// 						.getField());
// 				final String name = input.getName();
// 
// 				final boolean isResolved = relatedModule.isInputResolved(name);
// 
// 				// Include resolved status in the JSON feed.
// 				// This is handy for judiciously overwriting already-resolved inputs,
// 				// particularly the "active image" inputs, which will be reported as
// 				// resolved, but not necessarily match what's selected on the client
// 				// side.
// 				webCommandModuleItem.isResolved = isResolved;
// 
// 				// If the input is not resolved, include startingValue in the JSON feed.
// 				// This is useful for populating the dialog.
// 				webCommandModuleItem.startingValue = (!isResolved) ? relatedModule
// 					.getInput(name) : null;
// 
// 				checkedInputs.add(webCommandModuleItem);
// 			}
// 		}
// 
// 		return Collections.unmodifiableList(checkedInputs);
// 	}
// }
// 
// Path: src/main/java/net/imagej/server/services/JsonService.java
// public interface JsonService {
// 
// 	/**
// 	 * Adds a deserializer to a given {@link ObjectMapper} to modify the default
// 	 * behavior.
// 	 * 
// 	 * @param objectMapper
// 	 */
// 	void addDeserializerTo(final ObjectMapper objectMapper);
// 
// 	/**
// 	 * Parses the given Object.
// 	 * 
// 	 * @param obj Object to be parsed.
// 	 * @return a JSON format String of the parsed Object
// 	 * @throws JsonProcessingException
// 	 */
// 	String parseObject(final Object obj) throws JsonProcessingException;
// }


import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
import net.imagej.ops.Initializable;
import net.imagej.server.WebCommandInfo;
import net.imagej.server.services.JsonService;
import org.scijava.Context;
import org.scijava.Identifiable;
import org.scijava.Priority;
import org.scijava.command.CommandInfo;
import org.scijava.module.Module;
import org.scijava.module.ModuleInfo;
import org.scijava.module.ModuleService;
import org.scijava.module.process.AbstractPreprocessorPlugin;
import org.scijava.module.process.ModulePreprocessor;
import org.scijava.module.process.PreprocessorPlugin;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.plugin.PluginInfo;
import org.scijava.plugin.PluginService;
import org.scijava.widget.InputHarvester;
		// Check if we're dealing with Command information
		if (!(info instanceof CommandInfo)) {
			// TODO - decide what to do if this happens.
			throw new IllegalArgumentException("Object is not an instance of " + CommandInfo.class.getName());
		}

		// Create a transient instance of the module, so we can do some
		// selective preprocessing. This is necessary to determine which
		// inputs are still unresolved at the time of user input harvesting,
		// as well as what their current starting values are.
		final Module module = moduleService.createModule(info);

		// Get the complete list of preprocessors.
		final List<PluginInfo<PreprocessorPlugin>> allPPs =
			pluginService.getPluginsOfType(PreprocessorPlugin.class);

		// Filter the list to only those which run _before_ input harvesting.
		final List<PluginInfo<PreprocessorPlugin>> goodPPs = allPPs.stream() //
			.filter(ppInfo -> ppInfo.getPriority() > InputHarvester.PRIORITY) //
			.collect(Collectors.toList());

		// Execute all of these "good" preprocessors to prep the module correctly.
		for (final ModulePreprocessor p : pluginService.createInstances(goodPPs)) {
			p.process(module);
			if (p.isCanceled()) {
				// TODO - decide what to do if this happens.
			}
		}

		// Create a WebCommandInfo instance and parse it (resolved inputs will be identified during the process)
// Path: src/main/java/net/imagej/server/WebCommandInfo.java
// public class WebCommandInfo extends CommandInfo {
// 
// 	private final Module relatedModule;
// 
// 	public WebCommandInfo(CommandInfo commandInfo, Module module) {
// 		super(commandInfo);
// 		relatedModule = module;
// 	}
// 
// 	@Override
// 	public Iterable<ModuleItem<?>> inputs() {
// 
// 		final List<WebCommandModuleItem<?>> checkedInputs = new ArrayList<>();
// 		for (final ModuleItem<?> input : super.inputs()) {
// 			if (input instanceof CommandModuleItem) {
// 				final WebCommandModuleItem<Object> webCommandModuleItem =
// 					new WebCommandModuleItem<>(this, ((CommandModuleItem<?>) input)
// 						.getField());
// 				final String name = input.getName();
// 
// 				final boolean isResolved = relatedModule.isInputResolved(name);
// 
// 				// Include resolved status in the JSON feed.
// 				// This is handy for judiciously overwriting already-resolved inputs,
// 				// particularly the "active image" inputs, which will be reported as
// 				// resolved, but not necessarily match what's selected on the client
// 				// side.
// 				webCommandModuleItem.isResolved = isResolved;
// 
// 				// If the input is not resolved, include startingValue in the JSON feed.
// 				// This is useful for populating the dialog.
// 				webCommandModuleItem.startingValue = (!isResolved) ? relatedModule
// 					.getInput(name) : null;
// 
// 				checkedInputs.add(webCommandModuleItem);
// 			}
// 		}
// 
// 		return Collections.unmodifiableList(checkedInputs);
// 	}
// }
// 
// Path: src/main/java/net/imagej/server/services/JsonService.java
// public interface JsonService {
// 
// 	/**
// 	 * Adds a deserializer to a given {@link ObjectMapper} to modify the default
// 	 * behavior.
// 	 * 
// 	 * @param objectMapper
// 	 */
// 	void addDeserializerTo(final ObjectMapper objectMapper);
// 
// 	/**
// 	 * Parses the given Object.
// 	 * 
// 	 * @param obj Object to be parsed.
// 	 * @return a JSON format String of the parsed Object
// 	 * @throws JsonProcessingException
// 	 */
// 	String parseObject(final Object obj) throws JsonProcessingException;
// }


// Path: src/main/java/net/imagej/server/resources/ModulesResource.java
import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
import net.imagej.ops.Initializable;
import net.imagej.server.WebCommandInfo;
import net.imagej.server.services.JsonService;
import org.scijava.Context;
import org.scijava.Identifiable;
import org.scijava.Priority;
import org.scijava.command.CommandInfo;
import org.scijava.module.Module;
import org.scijava.module.ModuleInfo;
import org.scijava.module.ModuleService;
import org.scijava.module.process.AbstractPreprocessorPlugin;
import org.scijava.module.process.ModulePreprocessor;
import org.scijava.module.process.PreprocessorPlugin;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.plugin.PluginInfo;
import org.scijava.plugin.PluginService;
import org.scijava.widget.InputHarvester;

		// Check if we're dealing with Command information
		if (!(info instanceof CommandInfo)) {
			// TODO - decide what to do if this happens.
			throw new IllegalArgumentException("Object is not an instance of " + CommandInfo.class.getName());
		}

		// Create a transient instance of the module, so we can do some
		// selective preprocessing. This is necessary to determine which
		// inputs are still unresolved at the time of user input harvesting,
		// as well as what their current starting values are.
		final Module module = moduleService.createModule(info);

		// Get the complete list of preprocessors.
		final List<PluginInfo<PreprocessorPlugin>> allPPs =
			pluginService.getPluginsOfType(PreprocessorPlugin.class);

		// Filter the list to only those which run _before_ input harvesting.
		final List<PluginInfo<PreprocessorPlugin>> goodPPs = allPPs.stream() //
			.filter(ppInfo -> ppInfo.getPriority() > InputHarvester.PRIORITY) //
			.collect(Collectors.toList());

		// Execute all of these "good" preprocessors to prep the module correctly.
		for (final ModulePreprocessor p : pluginService.createInstances(goodPPs)) {
			p.process(module);
			if (p.isCanceled()) {
				// TODO - decide what to do if this happens.
			}
		}

		// Create a WebCommandInfo instance and parse it (resolved inputs will be identified during the process)
		return jsonService.parseObject(new WebCommandInfo((CommandInfo)info, module));