From 734b712aa4942add95c712b089d04aba689d0b49 Mon Sep 17 00:00:00 2001 From: Mateusz Warowny Date: Thu, 24 Mar 2022 13:14:11 +0100 Subject: [PATCH] JAL-3878 Create service agnostic param datastore and set --- src/jalview/ws2/params/SimpleParamDatastore.java | 256 ++++++++++++++++++++ src/jalview/ws2/params/SimpleParamSet.java | 281 ++++++++++++++++++++++ 2 files changed, 537 insertions(+) create mode 100644 src/jalview/ws2/params/SimpleParamDatastore.java create mode 100644 src/jalview/ws2/params/SimpleParamSet.java diff --git a/src/jalview/ws2/params/SimpleParamDatastore.java b/src/jalview/ws2/params/SimpleParamDatastore.java new file mode 100644 index 0000000..259bdca --- /dev/null +++ b/src/jalview/ws2/params/SimpleParamDatastore.java @@ -0,0 +1,256 @@ +package jalview.ws2.params; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +import jalview.bin.Cache; +import jalview.util.MessageManager; +import jalview.ws.params.ArgumentI; +import jalview.ws.params.ParamDatastoreI; +import jalview.ws.params.ParamManager; +import jalview.ws.params.WsParamSetI; + +/** + * A web client agnostic parameters datastore that provides view of the + * parameters and delegates parameters storage to {@link ParamManager} + * if given. Parameter datastore maintains the applicable service url + * the list of service parameters and both presets and user defined + * parameter sets + */ +public class SimpleParamDatastore implements ParamDatastoreI +{ + protected URL serviceUrl; + protected List parameters; + protected List servicePresets; + protected List userPresets = new ArrayList<>(); + protected ParamManager manager; + + /** + * Create new parameter datastore bound to the specified url with + * given service parameters and presets. Additionally, a parameters + * manager may be provided that will be used to load and store + * user parameter sets. + * + * @param serviceUrl applicable url + * @param parameters service parameters + * @param presets unmodifiable service presets + * @param manager parameter manager used to load and store user presets + */ + public SimpleParamDatastore(URL serviceUrl, List parameters, + List presets, ParamManager manager) + { + this.serviceUrl = serviceUrl; + this.parameters = Collections.unmodifiableList(new ArrayList<>(parameters)); + this.servicePresets = new ArrayList<>(presets.size()); + for (var preset : presets) + { + if (preset instanceof SimpleParamSet) + servicePresets.add((SimpleParamSet) preset); + else + servicePresets.add(new SimpleParamSet(preset)); + } + this.servicePresets = Collections.unmodifiableList(this.servicePresets); + this.manager = manager; + if (manager != null) + _initManager(manager); + } + + private void _initManager(ParamManager manager) + { + manager.registerParser(serviceUrl.toString(), this); + WsParamSetI[] paramSets = manager.getParameterSet(null, serviceUrl.toString(), + true, false); + if (paramSets != null) + { + for (WsParamSetI paramSet : paramSets) + { + // TODO: handle mismatch between preset and current parameters + if (paramSet instanceof SimpleParamSet) + userPresets.add((SimpleParamSet) paramSet); + else + { + userPresets.add(new SimpleParamSet(paramSet)); + Cache.log.warn(String.format( + "Parameter set instance type %s is not applicable to service" + + "at %s.", paramSet.getClass(), serviceUrl)); + } + } + } + } + + @Override + public List getPresets() + { + List presets = new ArrayList<>(); + presets.addAll(servicePresets); + presets.addAll(userPresets); + return presets; + } + + @Override + public SimpleParamSet getPreset(String name) + { + SimpleParamSet preset = null; + preset = getUserPreset(name); + if (preset != null) + return preset; + preset = getServicePreset(name); + if (preset != null) + return preset; + return null; + } + + public SimpleParamSet getUserPreset(String name) + { + for (SimpleParamSet preset : userPresets) + { + if (name.equals(preset.getName())) + return preset; + } + return null; + } + + public SimpleParamSet getServicePreset(String name) + { + for (SimpleParamSet preset : servicePresets) + { + if (name.equals(preset.getName())) + return preset; + } + return null; + } + + @Override + public List getServiceParameters() + { + return parameters; + } + + @Override + public boolean presetExists(String name) + { + return getPreset(name) != null; + } + + @Override + public void deletePreset(String name) + { + var userPreset = getUserPreset(name); + if (userPreset != null) + { + userPresets.remove(userPreset); + if (manager != null) + { + manager.deleteParameterSet(userPreset); + } + } + else if (getServicePreset(name) != null) + { + throw new RuntimeException(MessageManager.getString( + "error.implementation_error_attempt_to_delete_service_preset")); + } + else + { + Cache.log.warn("Implementation error: no preset to delete"); + } + } + + @Override + public void storePreset(String presetName, String text, List jobParams) + { + var builder = SimpleParamSet.newBuilder(); + builder.name(presetName); + builder.description(text); + builder.arguments(jobParams); + builder.url(serviceUrl.toString()); + builder.modifiable(true); + var preset = builder.build(); + userPresets.add(preset); + if (manager != null) + manager.storeParameterSet(preset); + } + + @Override + public void updatePreset(String oldName, String newName, String text, List jobParams) + { + var preset = getPreset(oldName != null ? oldName : newName); + if (preset == null) + throw new RuntimeException(MessageManager.formatMessage( + "error.implementation_error_cannot_locate_oldname_presetname", + oldName, newName)); + preset.setName(newName); + preset.setDescription(text); + preset.setArguments(jobParams); + preset.setApplicableUrls(new String[] { serviceUrl.toString() }); + if (manager != null) + manager.storeParameterSet(preset); + } + + @Override + public WsParamSetI parseServiceParameterFile(String name, String description, + String[] serviceURL, String parameters) + throws IOException + { + var builder = SimpleParamSet.newBuilder(); + builder.name(name); + builder.description(description); + builder.urls(serviceURL); + builder.modifiable(true); + Unmarshaller unmarshaller; + try + { + var ctx = JAXBContext.newInstance(ArgumentBeanList.class); + unmarshaller = ctx.createUnmarshaller(); + } catch (JAXBException e) + { + throw new RuntimeException(e); + } + ArgumentBeanList argList; + try + { + argList = (ArgumentBeanList) unmarshaller.unmarshal(new StringReader(parameters)); + } catch (JAXBException | ClassCastException e) + { + throw new IOException("Unable to load parameters from file", e); + } + builder.arguments(argList.arguments); + return builder.build(); + } + + @Override + public String generateServiceParameterFile(WsParamSetI pset) throws IOException + { + Marshaller marshaller; + try + { + var ctx = JAXBContext.newInstance(ArgumentBeanList.class); + marshaller = ctx.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); + } catch (JAXBException e) + { + throw new RuntimeException(e); + } + ArgumentBeanList argList = ArgumentBeanList.fromList(pset.getArguments()); + var out = new ByteArrayOutputStream(); + try + { + marshaller.marshal(argList, out); + } catch (JAXBException e) + { + throw new IOException("Unable to generate parameters file", e); + } + return out.toString(); + } + +} diff --git a/src/jalview/ws2/params/SimpleParamSet.java b/src/jalview/ws2/params/SimpleParamSet.java new file mode 100644 index 0000000..9050c5f --- /dev/null +++ b/src/jalview/ws2/params/SimpleParamSet.java @@ -0,0 +1,281 @@ +package jalview.ws2.params; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import jalview.ws.params.ArgumentI; +import jalview.ws.params.WsParamSetI; + +/** + * A simple, web service client agnostic, representation of parameter sets. + * Instances are created from the service data fetched from the server or from + * the user preset files. This implementation of {@link WsParamSetI} is meant to + * decouple parameter set representation form specific clients. + * + * @author mmwarowny + * + */ +public class SimpleParamSet implements WsParamSetI +{ + /** + * A convenience builder of {@link SimpleParamSet} objects. + * + * @author mmwarowny + */ + public static class Builder + { + private String name = "default"; + + private String description = ""; + + private List applicableUrls = new ArrayList<>(); + + private boolean modifiable = false; + + private List arguments = new ArrayList<>(); + + public Builder() + { + } + + /** + * Set a name of parameter set. + * + * @param val + * name + */ + public void name(String val) + { + name = val; + } + + /** + * Set a description of parameter set. + * + * @param val + * description + */ + public void description(String val) + { + description = val; + } + + /** + * Add a url to applicable urls for parameter set. + * + * @param val + * applicable url + */ + public void url(String val) + { + applicableUrls.add(val); + } + + /** + * Set all applicable urls for parameter set. Current url list will be + * replaced by provided urls. + * + * @param val + * applicable urls + */ + public void urls(String[] val) + { + applicableUrls.clear(); + for (String url : val) + applicableUrls.add(url); + } + + /** + * Set modifiable flag for parameter set. + * + * @param val + * modifiable + */ + public void modifiable(boolean val) + { + modifiable = val; + } + + /** + * Add an argument to the preset arguments. + * + * @param val + * argument to be added + */ + public void argument(ArgumentI val) + { + arguments.add(val); + } + + /** + * Set arguments for parameter set. Current parameters list will be + * replaced by provided arguments. + * + * @param val + * arguments to be added + */ + public void arguments(List val) + { + arguments.clear(); + arguments.addAll(val); + } + + /** + * Build a new {@link SimpleParamSet} object from the current state of this + * builder. + * + * @return new paramset instance + */ + public SimpleParamSet build() + { + return new SimpleParamSet(this); + } + } + + protected String name; + + protected String description; + + protected String[] applicableUrls; + + protected String sourceFile; + + protected boolean modifiable; + + protected List arguments; + + protected SimpleParamSet(Builder builder) + { + this.name = builder.name; + this.description = builder.description; + this.applicableUrls = builder.applicableUrls.toArray(new String[0]); + this.sourceFile = null; + this.modifiable = builder.modifiable; + setArguments(builder.arguments); + } + + /** + * Create a copy of the provided paramset. The new instance has the same + * properties as the original paramset. The arguments list is a shallow copy + * of the original arguments. + * + * @param copy + */ + public SimpleParamSet(WsParamSetI copy) + { + this.name = copy.getName(); + this.description = copy.getDescription(); + var urls = copy.getApplicableUrls(); + this.applicableUrls = Arrays.copyOf(urls, urls.length); + this.sourceFile = copy.getSourceFile(); + this.modifiable = copy.isModifiable(); + setArguments(copy.getArguments()); + } + + /** + * Create a new instance of the parameter set builder. + * + * @return new parameter set builder + */ + public static Builder newBuilder() + { + return new Builder(); + } + + @Override + public String getName() + { + return name; + } + + /** + * Set a human readable name for this parameter set. + * + * @param name + * new name + */ + public void setName(String name) + { + this.name = name; + } + + @Override + public String getDescription() + { + return description; + } + + /** + * Set additional notes for this parameter set. + * + * @param description + * additional notes + */ + public void setDescription(String description) + { + this.description = description; + } + + @Override + public String[] getApplicableUrls() + { + return applicableUrls; + } + + /** + * Set the list of service endpoints which this parameter set is valid for. + * + * @param urls + * new service endpoints + */ + public void setApplicableUrls(String[] urls) + { + this.applicableUrls = urls; + } + + @Override + public String getSourceFile() + { + return sourceFile; + } + + @Override + public void setSourceFile(String newFile) + { + this.sourceFile = newFile; + } + + @Override + public boolean isModifiable() + { + return this.modifiable; + } + + /** + * Set whether this parameter set is modifiable or not. + * + * @param modifiable + * new modifiable value + */ + public void setModifiable(boolean modifiable) + { + this.modifiable = modifiable; + } + + @Override + public List getArguments() + { + return this.arguments; + } + + @Override + public void setArguments(List args) + { + if (!isModifiable()) + throw new UnsupportedOperationException( + "Attempting to modify an unmodifiable parameter set"); + this.arguments = Collections.unmodifiableList(new ArrayList<>(args)); + } +} -- 1.7.10.2