diff --git a/README.adoc b/README.adoc index 1740b14..08c9bc1 100644 --- a/README.adoc +++ b/README.adoc @@ -16,7 +16,12 @@ java -jar tinypounderXXX.jar If you usually run the TinyPounder with the same kit, you can provide its path at start time : ---- -java -jar tinypounderXXX.jar --kit.path=/path/to/unzipped/kit/ +java -jar tinypounderXXX.jar --kitPath=/path/to/unzipped/kit/ --licensePath=/path/to/license.xml +---- + +Disable auto-launch browser (when you already have a tinyPounder window) +---- +java -jar tinypounderXXX.jar --autoLaunchBrowser=false ---- Then head to http://localhost:8080 diff --git a/src/main/java/org/terracotta/tinypounder/ApplicationReadyListener.java b/src/main/java/org/terracotta/tinypounder/ApplicationReadyListener.java index a942c43..9997b73 100644 --- a/src/main/java/org/terracotta/tinypounder/ApplicationReadyListener.java +++ b/src/main/java/org/terracotta/tinypounder/ApplicationReadyListener.java @@ -21,10 +21,14 @@ public class ApplicationReadyListener implements ApplicationListener cellDefinitionClass; - private Object typeString; private Class typeClass; + private Set customCells = (new ConcurrentHashMap<>()).newKeySet(); @Autowired public DatasetManagerBusinessReflectionImpl(KitAwareClassLoaderDelegator kitAwareClassLoaderDelegator, ScheduledExecutorService poundingScheduler) throws Exception { @@ -58,11 +50,11 @@ public DatasetManagerBusinessReflectionImpl(KitAwareClassLoaderDelegator kitAwar private void pound(Object datasetInstance, Integer intensity) { IntStream.range(0, intensity).forEach(value -> { - insert(datasetInstance, Long.toString(ThreadLocalRandom.current().nextLong(0, intensity * 1000)), longString(intensity)); - update(datasetInstance, Long.toString(ThreadLocalRandom.current().nextLong(0, intensity * 1000)), longString(intensity)); - delete(datasetInstance, Long.toString(ThreadLocalRandom.current().nextLong(0, intensity * 1000))); + insert(datasetInstance, ThreadLocalRandom.current().nextLong(0, intensity * 1000), intensity); + update(datasetInstance, ThreadLocalRandom.current().nextLong(0, intensity * 1000), intensity); + delete(datasetInstance, ThreadLocalRandom.current().nextLong(0, intensity * 1000)); stream(datasetInstance); - retrieve(datasetInstance, Long.toString(ThreadLocalRandom.current().nextLong(0, intensity * 1000))); + retrieve(datasetInstance, ThreadLocalRandom.current().nextLong(0, intensity * 1000)); }); try { generateRandomFailure(datasetInstance); @@ -75,10 +67,10 @@ private void generateRandomFailure(Object datasetInstance) { int nextInt = ThreadLocalRandom.current().nextInt(0, 5); switch (nextInt) { case 0: - insert(datasetInstance, null, longString(0)); + insert(datasetInstance, null, 0); break; case 1: - update(datasetInstance, null, longString(0)); + update(datasetInstance, null, 0); break; case 2: retrieve(datasetInstance, null); @@ -155,19 +147,19 @@ private void stream(Object datasetInstance) { } } - private void delete(Object datasetInstance, String key) { + private void delete(Object datasetInstance, Long key) { try { Thread.currentThread().setContextClassLoader(kitAwareClassLoaderDelegator.getUrlClassLoader()); Object datasetWriterReader = retrieveDatasetWriterReader(datasetInstance); Class datasetWriterReaderClass = loadClass("com.terracottatech.store.DatasetWriterReader"); Method deleteMethod = datasetWriterReaderClass.getMethod("delete", Comparable.class); - deleteMethod.invoke(datasetWriterReader, key); + deleteMethod.invoke(datasetWriterReader, longToKeyType(getKeyTypeJDK(datasetInstance), key)); } catch (Exception e) { throw new RuntimeException(e); } } - private void update(Object datasetInstance, String key, String value) { + private void update(Object datasetInstance, Long key, Integer value) { try { Thread.currentThread().setContextClassLoader(kitAwareClassLoaderDelegator.getUrlClassLoader()); Object datasetWriterReader = retrieveDatasetWriterReader(datasetInstance); @@ -175,23 +167,23 @@ private void update(Object datasetInstance, String key, String value) { Class updateOperationClass = loadClass("com.terracottatech.store.UpdateOperation"); Method writeMethod = updateOperationClass.getMethod("write", cellDefinitionClass, Object.class); - Object writeOperation = writeMethod.invoke(null, stringCellDefinition, value); + Object writeOperation = writeMethod.invoke(null, stringCellDefinition, longString(value)); Method updateMethod = datasetWriterReaderClass.getMethod("update", Comparable.class, updateOperationClass); - updateMethod.invoke(datasetWriterReader, key, writeOperation); + updateMethod.invoke(datasetWriterReader, longToKeyType(getKeyTypeJDK(datasetInstance), key), writeOperation); } catch (Exception e) { throw new RuntimeException(e); } } - private void retrieve(Object datasetInstance, String key) { + private void retrieve(Object datasetInstance, Long key) { try { Thread.currentThread().setContextClassLoader(kitAwareClassLoaderDelegator.getUrlClassLoader()); Object datasetWriterReader = retrieveDatasetWriterReader(datasetInstance); Class datasetWriterReaderClass = loadClass("com.terracottatech.store.DatasetWriterReader"); Method getMethod = datasetWriterReaderClass.getMethod("get", Comparable.class); - getMethod.invoke(datasetWriterReader, key); + getMethod.invoke(datasetWriterReader, longToKeyType(getKeyTypeJDK(datasetInstance), key)); } catch (Exception e) { throw new RuntimeException(e); } @@ -199,63 +191,84 @@ private void retrieve(Object datasetInstance, String key) { private Object retrieveDatasetWriterReader(Object datasetInstance) throws Exception { Class internalDatasetClass = loadClass("com.terracottatech.store.internal.InternalDataset"); + Method writerReader = internalDatasetClass.getMethod("writerReader"); + return writerReader.invoke(datasetInstance); + } + private Object retrieveDatasetReader(Object datasetInstance) throws Exception { + Class internalDatasetClass = loadClass("com.terracottatech.store.internal.InternalDataset"); + Method reader = internalDatasetClass.getMethod("reader"); + return reader.invoke(datasetInstance); + } - Class readVisibilityClas = loadClass("com.terracottatech.store.setting.ReadVisibility"); - Class readSettingsClass = loadClass("com.terracottatech.store.setting.ReadSettings"); - - Class writeVisibilityClas = loadClass("com.terracottatech.store.setting.WriteVisibility"); - Class writeSettingsClass = loadClass("com.terracottatech.store.setting.WriteSettings"); - - - Field definitiveField = readVisibilityClas.getDeclaredField("DEFINITIVE"); - Object definitiveReadVisibility = definitiveField.get(null); - Method asReadSettingsMethod = readVisibilityClas.getMethod("asReadSettings"); - Object definitiveReadVisibilityAsReadSettings = asReadSettingsMethod.invoke(definitiveReadVisibility); - - - Field immediateField = writeVisibilityClas.getDeclaredField("IMMEDIATE"); - Object immediateWriteVisibility = immediateField.get(null); - Method asWriteSettingsMethod = writeVisibilityClas.getMethod("asWriteSettings"); - Object immediateWriteVisibilityAsWriteSettings = asWriteSettingsMethod.invoke(immediateWriteVisibility); - - Method writerReader = internalDatasetClass.getMethod("writerReader", readSettingsClass, writeSettingsClass); + private Object longToKeyType(Class keyTypeJDK, Long internalKey) { + Object key; + if (keyTypeJDK.equals(String.class)) { + key = Long.toString(internalKey); + } else if (keyTypeJDK.equals(Integer.class)) { + key = internalKey.intValue(); + } else if (keyTypeJDK.equals(Long.class)) { + key = internalKey; + } else if (keyTypeJDK.equals(Double.class)) { + key = internalKey.doubleValue(); + } else if (keyTypeJDK.equals(Boolean.class)) { + key = internalKey % 2 == 0; + } else if (keyTypeJDK.equals(Character.class)) { + key = Long.toString(internalKey).charAt(0); + } else { + key = null; + } + return key; + } - return writerReader.invoke(datasetInstance, definitiveReadVisibilityAsReadSettings, immediateWriteVisibilityAsWriteSettings); + private Class getKeyTypeJDK(Object datasetInstance) throws Exception { + Class datasetReaderClass = loadClass("com.terracottatech.store.DatasetReader"); + Method getKeyTypeMethod = datasetReaderClass.getMethod("getKeyType"); + Object datasetReader = retrieveDatasetReader(datasetInstance); + Object keyType = getKeyTypeMethod.invoke(datasetReader); + Method getJDKTypeMethod = typeClass.getDeclaredMethod("getJDKType"); + return (Class) getJDKTypeMethod.invoke(keyType); } - private void insert(Object datasetInstance, String key, String value) { + private void insert(Object datasetInstance, Long key, Integer value) { try { Thread.currentThread().setContextClassLoader(kitAwareClassLoaderDelegator.getUrlClassLoader()); Object datasetWriterReader = retrieveDatasetWriterReader(datasetInstance); - Class datasetWriterReaderClass = loadClass("com.terracottatech.store.DatasetWriterReader"); Class cellClass = loadClass("com.terracottatech.store.Cell"); Class cellClasses = Array.newInstance(cellClass, 0).getClass(); Method addMethod = datasetWriterReaderClass.getMethod("add", Comparable.class, cellClasses); - Object[] cells; - if (key == null || Long.parseLong(key) % 2 == 0) { - cells = generateOneCell(value, cellClass); + Object[] generatedCells; + if (key == null || key % 2 == 0) { + generatedCells = generateOneCell(value, cellClass); } else { - cells = generateTwoCells(value, cellClass); + generatedCells = generateTwoCells(value, cellClass); } - - addMethod.invoke(datasetWriterReader, key, cells); - + Object[] customCells = generateCustomCells(value); + Object[] cells = (Object[]) Array.newInstance(cellClass, generatedCells.length + customCells.length); + int i = 0; + for (Object c : generatedCells) { + cells[i++] = c; + } + for (Object c : customCells) { + cells[i++] = c; + } + addMethod.invoke(datasetWriterReader, longToKeyType(getKeyTypeJDK(datasetInstance), key), cells); } catch (Exception e) { throw new RuntimeException(e); } } - private Object[] generateTwoCells(String value, Class cellClass) throws Exception { + private Object[] generateTwoCells(Integer value, Class cellClass) throws Exception { + String valueStr = longString(value); Method newCellMethod = cellDefinitionClass.getMethod("newCell", Object.class); - Object stringCellValue = newCellMethod.invoke(stringCellDefinition, value); + Object stringCellValue = newCellMethod.invoke(stringCellDefinition, valueStr); Method defineBytesMethod = cellDefinitionClass.getMethod("defineBytes", String.class); Object bytesCellDefinition = defineBytesMethod.invoke(null, "myBytesCell"); - Object bytesCellValue = newCellMethod.invoke(bytesCellDefinition, value.getBytes()); + Object bytesCellValue = newCellMethod.invoke(bytesCellDefinition, valueStr.getBytes()); Object[] cells = (Object[]) Array.newInstance(cellClass, 2); cells[0] = stringCellValue; @@ -263,18 +276,85 @@ private Object[] generateTwoCells(String value, Class cellClass) throws Excep return cells; } - private Object[] generateOneCell(String value, Class cellClass) throws Exception { + private Object[] generateOneCell(Integer value, Class cellClass) throws Exception { + String valueStr = longString(value); Class cellDefinitionClass = loadClass("com.terracottatech.store.definition.CellDefinition"); Method newCellMethod = cellDefinitionClass.getMethod("newCell", Object.class); Method defineBytesMethod = cellDefinitionClass.getMethod("defineBytes", String.class); Object bytesCellDefinition = defineBytesMethod.invoke(null, "myBytesCell"); - Object bytesCellValue = newCellMethod.invoke(bytesCellDefinition, value.getBytes()); + Object bytesCellValue = newCellMethod.invoke(bytesCellDefinition, valueStr.getBytes()); Object[] cells = (Object[]) Array.newInstance(cellClass, 1); cells[0] = bytesCellValue; return cells; } + private Object[] generateCustomCells(Integer value) throws Exception { + String valueStr = longString(value); + Method newCellMethod = cellDefinitionClass.getMethod("newCell", Object.class); + List cells = new ArrayList<>(); + for (String customCellStr : customCells) { + String[] splitted = customCellStr.split(":"); + String cellName = splitted[0]; + String cellType = splitted[1]; + switch(cellType) { + case "STRING": + Object stringCellDefinition = cellDefinitionClass + .getMethod("defineString", String.class).invoke(null, cellName); + cells.add(newCellMethod.invoke(stringCellDefinition, valueStr)); + break; + case "INT": + Object intCellDefinition = cellDefinitionClass + .getMethod("defineInt", String.class).invoke(null, cellName); + cells.add(newCellMethod.invoke(intCellDefinition, ThreadLocalRandom.current().nextInt(0, value * 1000))); + break; + case "LONG": + Object longCellDefinition = cellDefinitionClass + .getMethod("defineLong", String.class).invoke(null, cellName); + cells.add(newCellMethod.invoke(longCellDefinition, ThreadLocalRandom.current().nextLong(0, value * 1000))); + break; + case "DOUBLE": + Object doubleCellDefinition = cellDefinitionClass + .getMethod("defineDouble", String.class).invoke(null, cellName); + cells.add(newCellMethod.invoke(doubleCellDefinition, ThreadLocalRandom.current().nextDouble(0, value * 1000))); + break; + case "BOOL": + Object boolCellDefinition = cellDefinitionClass + .getMethod("defineBool", String.class).invoke(null, cellName); + cells.add(newCellMethod.invoke(boolCellDefinition, ThreadLocalRandom.current().nextInt(0, value * 1000) % 2 == 0)); + break; + case "CHAR": + Object charCellDefinition = cellDefinitionClass + .getMethod("defineChar", String.class).invoke(null, cellName); + cells.add(newCellMethod.invoke(charCellDefinition, valueStr.charAt(0))); + break; + case "BYTES": + Object bytesCellDefinition = cellDefinitionClass + .getMethod("defineBytes", String.class).invoke(null, cellName); + cells.add(newCellMethod.invoke(bytesCellDefinition, valueStr.getBytes())); + break; + default: + throw new RuntimeException("Cannot recognize cell type: " + cellType); + } + } + return cells.toArray(); + } + + public void addCustomCell(String cellStr) { + String[] splitted = cellStr.split(":"); + if (splitted.length == 2) { + String cellType = splitted[1]; + Set TYPES = new HashSet<>(Arrays.asList("STRING", "INT", "LONG", "DOUBLE", "BOOL", "CHAR", "BYTES")); + if (TYPES.contains(cellType)) { + customCells.add(cellStr); + } + } + } + + public void removeCustomCell(String cellStr) { + customCells.remove(cellStr); + } + private String longString(Integer intensity) { return new BigInteger(intensity * 10, random).toString(16); } @@ -334,8 +414,6 @@ public void initializeDatasetManager(String terracottaServerUrl) { private void initCommonObjectsAndClasses() throws Exception { typeClass = loadClass("com.terracottatech.store.Type"); - Field typeStringField = typeClass.getDeclaredField("STRING"); - typeString = typeStringField.get(null); cellDefinitionClass = loadClass("com.terracottatech.store.definition.CellDefinition"); Method defineStringMethod = cellDefinitionClass.getMethod("defineString", String.class); @@ -403,7 +481,7 @@ public void createDataset(String datasetName, DatasetConfiguration datasetConfig Object datasetConfigurationBuilt = buildMethod.invoke(datasetConfigurationBuilder); Method createDatasetMethod = datasetManagerClass.getMethod("createDataset", String.class, typeClass, datasetConfigurationClass); - Object datasetInstance = createDatasetMethod.invoke(datasetManager, datasetName, typeString, datasetConfigurationBuilt); + Object datasetInstance = createDatasetMethod.invoke(datasetManager, datasetName, toKeyType(datasetConfiguration.getKeyType()), datasetConfigurationBuilt); String instanceName = getInstanceName(datasetInstance); Map instancesByName = new TreeMap<>(); @@ -414,6 +492,10 @@ public void createDataset(String datasetName, DatasetConfiguration datasetConfig } } + private Object toKeyType(String keyTypeStr) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { + return loadClass("com.terracottatech.store.Type").getDeclaredField(keyTypeStr).get(null); + } + private String getInstanceName(Object datasetInstance) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { Class manageableDatasetClass = loadClass("com.terracottatech.store.internal.InternalDataset"); Method getStatisticsMethod = manageableDatasetClass.getMethod("getStatistics"); @@ -478,9 +560,10 @@ public void closeDatasetInstance(String datasetName, String instanceName) { public String createDatasetInstance(String datasetName) { try { Thread.currentThread().setContextClassLoader(kitAwareClassLoaderDelegator.getUrlClassLoader()); - + Method listDatasetsMethod = datasetManagerClass.getMethod("listDatasets"); + Map datasetsByName = (Map) listDatasetsMethod.invoke(datasetManager); Method getDatasetMethod = datasetManagerClass.getMethod("getDataset", String.class, typeClass); - Object datasetInstance = getDatasetMethod.invoke(datasetManager, datasetName, typeString); + Object datasetInstance = getDatasetMethod.invoke(datasetManager, datasetName, datasetsByName.get(datasetName)); String instanceName = getInstanceName(datasetInstance); datasetInstancesByDatasetName.computeIfAbsent(datasetName, k -> new TreeMap<>()); diff --git a/src/main/java/org/terracotta/tinypounder/TinyPounderMainUI.java b/src/main/java/org/terracotta/tinypounder/TinyPounderMainUI.java index b016ace..7e158a6 100644 --- a/src/main/java/org/terracotta/tinypounder/TinyPounderMainUI.java +++ b/src/main/java/org/terracotta/tinypounder/TinyPounderMainUI.java @@ -1489,9 +1489,18 @@ private void addDatasetControls() { datasetNameField.setPlaceholder("dataset name"); datasetNameField.addStyleName("align-bottom"); + List keyTypeValues = Arrays.asList("STRING", "INT", "LONG", "DOUBLE", "BOOL", "CHAR"); + ComboBox keyTypeComboBox = new ComboBox<>("Key type", keyTypeValues); + keyTypeComboBox.setStyleName("datasetAttribute"); + keyTypeComboBox.setEmptySelectionAllowed(false); + keyTypeComboBox.setTextInputAllowed(false); + keyTypeComboBox.setValue(keyTypeValues.get(0)); + + HorizontalLayout offHeapOption = new HorizontalLayout(); + offHeapOption.setStyleName("datasetAttribute"); TextField offHeapPersistenceLocationField = new TextField(); CheckBox offHeapCheckBox = new CheckBox("offheap", true); - offHeapCheckBox.addStyleName("shift-bottom-right-offheap"); + offHeapCheckBox.addStyleName("bottom"); offHeapCheckBox.addValueChangeListener(valueChangeEvent -> { if (valueChangeEvent.getValue()) { offHeapPersistenceLocationField.setEnabled(true); @@ -1501,11 +1510,13 @@ private void addDatasetControls() { }); offHeapPersistenceLocationField.setCaption("offheap resource name"); offHeapPersistenceLocationField.setValue("offheap-1"); + offHeapOption.addComponents(offHeapCheckBox, offHeapPersistenceLocationField); - + HorizontalLayout diskOption = new HorizontalLayout(); + diskOption.setStyleName("datasetAttribute"); TextField diskPersistenceLocationField = new TextField(); CheckBox diskCheckBox = new CheckBox("disk", true); - diskCheckBox.addStyleName("shift-bottom-right-disk"); + diskCheckBox.addStyleName("bottom"); diskCheckBox.addValueChangeListener(valueChangeEvent -> { if (valueChangeEvent.getValue()) { diskPersistenceLocationField.setEnabled(true); @@ -1515,18 +1526,20 @@ private void addDatasetControls() { }); diskPersistenceLocationField.setCaption("disk resource name"); diskPersistenceLocationField.setValue("dataroot-1"); + diskOption.addComponents(diskCheckBox, diskPersistenceLocationField); CheckBox indexCheckBox = new CheckBox("use index", true); - indexCheckBox.addStyleName("shift-bottom-right-index"); + indexCheckBox.setStyleName("datasetAttribute"); + indexCheckBox.addStyleName("bottom"); Button addDatasetButton = new Button("Add dataset"); - addDatasetButton.addStyleName("align-bottom"); + addDatasetButton.setStyleName("datasetAttribute"); - datasetCreation.addComponentsAndExpand(datasetNameField, offHeapCheckBox, offHeapPersistenceLocationField, diskCheckBox, diskPersistenceLocationField, indexCheckBox, addDatasetButton); + datasetCreation.addComponents(datasetNameField, keyTypeComboBox, offHeapOption, diskOption, indexCheckBox, addDatasetButton); ListDataProvider listDataProvider = new ListDataProvider<>(datasetNames); addDatasetButton.addClickListener(clickEvent -> { try { - DatasetConfiguration datasetConfiguration = new DatasetConfiguration(offHeapCheckBox.getValue() ? offHeapPersistenceLocationField.getValue() : null, diskCheckBox.getValue() ? diskPersistenceLocationField.getValue() : null, indexCheckBox.getValue()); + DatasetConfiguration datasetConfiguration = new DatasetConfiguration(keyTypeComboBox.getValue(), offHeapCheckBox.getValue() ? offHeapPersistenceLocationField.getValue() : null, diskCheckBox.getValue() ? diskPersistenceLocationField.getValue() : null, indexCheckBox.getValue()); datasetManagerBusiness.createDataset(datasetNameField.getValue(), datasetConfiguration); datasetNames.add(datasetNameField.getValue()); refreshDatasetStuff(listDataProvider); @@ -1574,10 +1587,40 @@ private void addDatasetControls() { if (datasetInstanceNames.size() > 0) { destroyDatasetButton.setEnabled(false); } + int count = 0; for (String instanceName : datasetInstanceNames) { HorizontalLayout datasetInstanceInfoLayout = new HorizontalLayout(); + if ((count++) % 2 == 0) { + datasetInstanceInfoLayout.setStyleName("greyBackground"); + } Label datasetInstanceNameLabel = new Label(instanceName); + datasetInstanceNameLabel.setStyleName("instance"); + + TextField newCellField = new TextField(); + newCellField.setPlaceholder("myCellName:STRING"); + newCellField.setStyleName("instance"); + Button addCellButton = new Button("Add cell"); + addCellButton.setStyleName("instance"); + addCellButton.addClickListener(event -> { + try { + datasetManagerBusiness.addCustomCell(newCellField.getValue()); + displayWarningNotification("New cell is added."); + } catch (Exception e) { + displayErrorNotification("New cell cannot be added.", e); + } + }); + Button removeCellButton = new Button("Remove cell"); + removeCellButton.setStyleName("instance"); + removeCellButton.addClickListener(event -> { + try { + datasetManagerBusiness.removeCustomCell(newCellField.getValue()); + displayWarningNotification("New cell is removed."); + } catch (Exception e) { + displayErrorNotification("New cell cannot be removed.", e); + } + }); Button closeDatasetButton = new Button("Close dataset instance"); + closeDatasetButton.setStyleName("instance"); closeDatasetButton.addClickListener(event -> { try { datasetManagerBusiness.closeDatasetInstance(datasetName, instanceName); @@ -1605,7 +1648,7 @@ private void addDatasetControls() { }); - datasetInstanceInfoLayout.addComponentsAndExpand(datasetInstanceNameLabel, poundingSlider, closeDatasetButton); + datasetInstanceInfoLayout.addComponentsAndExpand(datasetInstanceNameLabel, newCellField, addCellButton, removeCellButton, poundingSlider, closeDatasetButton); datasetListLayout.addComponent(datasetInstanceInfoLayout); } diff --git a/src/main/resources/VAADIN/themes/tinypounder/styles.scss b/src/main/resources/VAADIN/themes/tinypounder/styles.scss index df58974..92f0103 100644 --- a/src/main/resources/VAADIN/themes/tinypounder/styles.scss +++ b/src/main/resources/VAADIN/themes/tinypounder/styles.scss @@ -57,17 +57,42 @@ $v-layout-margin-bottom: 16px; .v-slot-shift-bottom-right-offheap { top: 28px; - left: 8%; + left: 5em; } .v-slot-shift-bottom-right-disk { top: 28px; - left: 10%; + left: 5em; } .v-slot-shift-bottom-right-index { top: 28px; - left: 6%; + left: 2em; +} + +.v-slot-datasetAttribute { + margin-left: 3em; +} + +.v-checkbox-bottom { + top: 2em; +} + +.v-button-datasetAttribute { + top: 1em; +} + +.v-label-instance { + margin-top: .5em; + margin-left: 1em; +} + +.v-slot-greyBackground { + background-color: #EAEAEA; +} + +.v-slot-instance { + top: .7em; } .tc-config-xml { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 49d5cae..3557bf4 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -10,3 +10,4 @@ reconnectWindow=0 spring.devtools.livereload.enabled=false spring.devtools.restart.enabled=false server.port=9490 +autoLaunchBrowser=true