From c7cfb533b261b7e36afd78b2061d48d2f55c4736 Mon Sep 17 00:00:00 2001 From: bencomp Date: Mon, 3 Oct 2022 01:17:42 +0200 Subject: [PATCH 01/30] Remove unused GPL-licensed code For unknown reasons, in 2009 several files from the JDK were copied into the Dataverse codebase, instead of referenced. It appears that these classes weren't really used. --- .../spi/FileMetadataExtractorSpi.java | 18 - .../ingest/plugin/spi/DigraphNode.java | 188 ---- .../plugin/spi/IngestServiceProvider.java | 25 +- .../plugin/spi/PartiallyOrderedSet.java | 241 ----- .../plugin/spi/RegisterableService.java | 90 -- .../ingest/plugin/spi/ServiceRegistry.java | 861 ------------------ 6 files changed, 1 insertion(+), 1422 deletions(-) delete mode 100644 src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/DigraphNode.java delete mode 100644 src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/PartiallyOrderedSet.java delete mode 100644 src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/RegisterableService.java delete mode 100644 src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/ServiceRegistry.java diff --git a/src/main/java/edu/harvard/iq/dataverse/ingest/metadataextraction/spi/FileMetadataExtractorSpi.java b/src/main/java/edu/harvard/iq/dataverse/ingest/metadataextraction/spi/FileMetadataExtractorSpi.java index ab8f610cb06..a30dfafe67f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/ingest/metadataextraction/spi/FileMetadataExtractorSpi.java +++ b/src/main/java/edu/harvard/iq/dataverse/ingest/metadataextraction/spi/FileMetadataExtractorSpi.java @@ -10,9 +10,7 @@ import java.util.logging.*; import java.io.*; -import edu.harvard.iq.dataverse.ingest.plugin.spi.RegisterableService; import edu.harvard.iq.dataverse.ingest.plugin.spi.IngestServiceProvider; -import edu.harvard.iq.dataverse.ingest.plugin.spi.ServiceRegistry; import java.nio.MappedByteBuffer; import java.util.Locale; @@ -44,22 +42,6 @@ public FileMetadataExtractorSpi(String vendorName, String version) { this.version = version; } - public void onRegistration(ServiceRegistry registry, - Class category) {} - - - public void onDeregistration(ServiceRegistry registry, - Class category) {} - - public String getVersion() { - return version; - } - - public String getVendorName() { - return vendorName; - } - - public abstract String getDescription(Locale locale); protected String[] names = null; diff --git a/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/DigraphNode.java b/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/DigraphNode.java deleted file mode 100644 index 4db48b5c06a..00000000000 --- a/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/DigraphNode.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - Copyright (C) 2005-2012, by the President and Fellows of Harvard College. - - Licensed 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. - - Dataverse Network - A web application to share, preserve and analyze research data. - Developed at the Institute for Quantitative Social Science, Harvard University. - Version 3.0. -*/ - -package edu.harvard.iq.dataverse.ingest.plugin.spi; - -// This file was Taken out from openjdk-6-src-b16-24_apr_2009.tar.gz -// http://download.java.net/openjdk/jdk6/promoted/b16/openjdk-6-src-b16-24_apr_2009.tar.gz -// downloaded: 2009-05-07 - - -/* - * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -//package javax.imageio.spi; - -import java.io.Serializable; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -/** - * A node in a directed graph. In addition to an arbitrary - * Object containing user data associated with the node, - * each node maintains a Sets of nodes which are pointed - * to by the current node (available from getOutNodes). - * The in-degree of the node (that is, number of nodes that point to - * the current node) may be queried. - * - */ -class DigraphNode implements Cloneable, Serializable { - - /** The data associated with this node. */ - protected Object data; - - /** - * A Set of neighboring nodes pointed to by this - * node. - */ - protected Set outNodes = new HashSet(); - - /** The in-degree of the node. */ - protected int inDegree = 0; - - /** - * A Set of neighboring nodes that point to this - * node. - */ - private Set inNodes = new HashSet(); - - public DigraphNode(Object data) { - this.data = data; - } - - /** Returns the Object referenced by this node. */ - public Object getData() { - return data; - } - - /** - * Returns an Iterator containing the nodes pointed - * to by this node. - */ - public Iterator getOutNodes() { - return outNodes.iterator(); - } - - /** - * Adds a directed edge to the graph. The outNodes list of this - * node is updated and the in-degree of the other node is incremented. - * - * @param node a DigraphNode. - * - * @return true if the node was not previously the - * target of an edge. - */ - public boolean addEdge(DigraphNode node) { - if (outNodes.contains(node)) { - return false; - } - - outNodes.add(node); - node.inNodes.add(this); - node.incrementInDegree(); - return true; - } - - /** - * Returns true if an edge exists between this node - * and the given node. - * - * @param node a DigraphNode. - * - * @return true if the node is the target of an edge. - */ - public boolean hasEdge(DigraphNode node) { - return outNodes.contains(node); - } - - /** - * Removes a directed edge from the graph. The outNodes list of this - * node is updated and the in-degree of the other node is decremented. - * - * @return true if the node was previously the target - * of an edge. - */ - public boolean removeEdge(DigraphNode node) { - if (!outNodes.contains(node)) { - return false; - } - - outNodes.remove(node); - node.inNodes.remove(this); - node.decrementInDegree(); - return true; - } - - /** - * Removes this node from the graph, updating neighboring nodes - * appropriately. - */ - public void dispose() { - Object[] inNodesArray = inNodes.toArray(); - for(int i=0; iServiceRegistry. - * - * @param registry the ServiceRegistry instance. - * @param category a Class object that indicatges - * its registry category under which this object has been registered. - * category. - */ - public void onRegistration(ServiceRegistry registry, - Class category) {} - - /** - * A callback whenever this Spi class is deregistered from - * a ServiceRegistry. - * - * @param registry the ServiceRegistry instance. - * @param category a Class object that indicatges - * its registry category from which this object is being de-registered. - */ - public void onDeregistration(ServiceRegistry registry, - Class category) {} /** * Gets the value of the version field. * diff --git a/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/PartiallyOrderedSet.java b/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/PartiallyOrderedSet.java deleted file mode 100644 index 87f4f57cdb6..00000000000 --- a/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/PartiallyOrderedSet.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - Copyright (C) 2005-2012, by the President and Fellows of Harvard College. - - Licensed 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. - - Dataverse Network - A web application to share, preserve and analyze research data. - Developed at the Institute for Quantitative Social Science, Harvard University. - Version 3.0. -*/ - -package edu.harvard.iq.dataverse.ingest.plugin.spi; - -// This file was Taken out from openjdk-6-src-b16-24_apr_2009.tar.gz -// http://download.java.net/openjdk/jdk6/promoted/b16/openjdk-6-src-b16-24_apr_2009.tar.gz -// downloaded: 2009-05-07 - - -/* - * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -//package javax.imageio.spi; - -import java.util.AbstractSet; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.Set; - -/** - * A set of Objects with pairwise orderings between them. - * The iterator method provides the elements in - * topologically sorted order. Elements participating in a cycle - * are not returned. - * - * Unlike the SortedSet and SortedMap - * interfaces, which require their elements to implement the - * Comparable interface, this class receives ordering - * information via its setOrdering and - * unsetPreference methods. This difference is due to - * the fact that the relevant ordering between elements is unlikely to - * be inherent in the elements themselves; rather, it is set - * dynamically accoring to application policy. For example, in a - * service provider registry situation, an application might allow the - * user to set a preference order for service provider objects - * supplied by a trusted vendor over those supplied by another. - * - */ -class PartiallyOrderedSet extends AbstractSet { - - // The topological sort (roughly) follows the algorithm described in - // Horowitz and Sahni, _Fundamentals of Data Structures_ (1976), - // p. 315. - - // Maps Objects to DigraphNodes that contain them - private Map poNodes = new HashMap(); - - // The set of Objects - private Set nodes = poNodes.keySet(); - - /** - * Constructs a PartiallyOrderedSet. - */ - public PartiallyOrderedSet() {} - - public int size() { - return nodes.size(); - } - - public boolean contains(Object o) { - return nodes.contains(o); - } - - /** - * Returns an iterator over the elements contained in this - * collection, with an ordering that respects the orderings set - * by the setOrdering method. - */ - public Iterator iterator() { - return new PartialOrderIterator(poNodes.values().iterator()); - } - - /** - * Adds an Object to this - * PartiallyOrderedSet. - */ - public boolean add(Object o) { - if (nodes.contains(o)) { - return false; - } - - DigraphNode node = new DigraphNode(o); - poNodes.put(o, node); - return true; - } - - /** - * Removes an Object from this - * PartiallyOrderedSet. - */ - public boolean remove(Object o) { - DigraphNode node = (DigraphNode)poNodes.get(o); - if (node == null) { - return false; - } - - poNodes.remove(o); - node.dispose(); - return true; - } - - public void clear() { - poNodes.clear(); - } - - /** - * Sets an ordering between two nodes. When an iterator is - * requested, the first node will appear earlier in the - * sequence than the second node. If a prior ordering existed - * between the nodes in the opposite order, it is removed. - * - * @return true if no prior ordering existed - * between the nodes, falseotherwise. - */ - public boolean setOrdering(Object first, Object second) { - DigraphNode firstPONode = - (DigraphNode)poNodes.get(first); - DigraphNode secondPONode = - (DigraphNode)poNodes.get(second); - - secondPONode.removeEdge(firstPONode); - return firstPONode.addEdge(secondPONode); - } - - /** - * Removes any ordering between two nodes. - * - * @return true if a prior prefence existed between the nodes. - */ - public boolean unsetOrdering(Object first, Object second) { - DigraphNode firstPONode = - (DigraphNode)poNodes.get(first); - DigraphNode secondPONode = - (DigraphNode)poNodes.get(second); - - return firstPONode.removeEdge(secondPONode) || - secondPONode.removeEdge(firstPONode); - } - - /** - * Returns true if an ordering exists between two - * nodes. - */ - public boolean hasOrdering(Object preferred, Object other) { - DigraphNode preferredPONode = - (DigraphNode)poNodes.get(preferred); - DigraphNode otherPONode = - (DigraphNode)poNodes.get(other); - - return preferredPONode.hasEdge(otherPONode); - } -} - -class PartialOrderIterator implements Iterator { - - LinkedList zeroList = new LinkedList(); - Map inDegrees = new HashMap(); // DigraphNode -> Integer - - public PartialOrderIterator(Iterator iter) { - // Initialize scratch in-degree values, zero list - while (iter.hasNext()) { - DigraphNode node = (DigraphNode)iter.next(); - int inDegree = node.getInDegree(); - inDegrees.put(node, new Integer(inDegree)); - - // Add nodes with zero in-degree to the zero list - if (inDegree == 0) { - zeroList.add(node); - } - } - } - - public boolean hasNext() { - return !zeroList.isEmpty(); - } - - public Object next() { - DigraphNode first = (DigraphNode)zeroList.removeFirst(); - - // For each out node of the output node, decrement its in-degree - Iterator outNodes = first.getOutNodes(); - while (outNodes.hasNext()) { - DigraphNode node = (DigraphNode)outNodes.next(); - int inDegree = ((Integer)inDegrees.get(node)).intValue() - 1; - inDegrees.put(node, new Integer(inDegree)); - - // If the in-degree has fallen to 0, place the node on the list - if (inDegree == 0) { - zeroList.add(node); - } - } - - return first.getData(); - } - - public void remove() { - throw new UnsupportedOperationException(); - } -} diff --git a/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/RegisterableService.java b/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/RegisterableService.java deleted file mode 100644 index d3609b1e4b9..00000000000 --- a/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/RegisterableService.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright (C) 2005-2012, by the President and Fellows of Harvard College. - - Licensed 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. - - Dataverse Network - A web application to share, preserve and analyze research data. - Developed at the Institute for Quantitative Social Science, Harvard University. - Version 3.0. -*/ - -package edu.harvard.iq.dataverse.ingest.plugin.spi; - -// This file was Taken out from openjdk-6-src-b16-24_apr_2009.tar.gz -// http://download.java.net/openjdk/jdk6/promoted/b16/openjdk-6-src-b16-24_apr_2009.tar.gz -// downloaded: 2009-05-07 - - -/* - * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - - -/** - * An optional interface that may be provided by service provider - * objects that will be registered with a - * ServiceRegistry. If this interface is present, - * notification of registration and deregistration will be performed. - * - * @see ServiceRegistry - * - */ -public interface RegisterableService { - - /** - * Called when an object implementing this interface is added to - * the given category of the given - * registry. The object may already be registered - * under another category or categories. - * - * @param registry a ServiceRegistry where this - * object has been registered. - * @param category a Class object indicating the - * registry category under which this object has been registered. - */ - void onRegistration(ServiceRegistry registry, Class category); - - /** - * Called when an object implementing this interface is removed - * from the given category of the given - * registry. The object may still be registered - * under another category or categories. - * - * @param registry a ServiceRegistry from which this - * object is being (wholly or partially) deregistered. - * @param category a Class object indicating the - * registry category from which this object is being deregistered. - */ - void onDeregistration(ServiceRegistry registry, Class category); -} diff --git a/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/ServiceRegistry.java b/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/ServiceRegistry.java deleted file mode 100644 index 1794adb5de2..00000000000 --- a/src/main/java/edu/harvard/iq/dataverse/ingest/plugin/spi/ServiceRegistry.java +++ /dev/null @@ -1,861 +0,0 @@ -/* - Copyright (C) 2005-2012, by the President and Fellows of Harvard College. - - Licensed 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. - - Dataverse Network - A web application to share, preserve and analyze research data. - Developed at the Institute for Quantitative Social Science, Harvard University. - Version 3.0. -*/ -package edu.harvard.iq.dataverse.ingest.plugin.spi; - - - -// This file was Taken out from openjdk-6-src-b16-24_apr_2009.tar.gz -// http://download.java.net/openjdk/jdk6/promoted/b16/openjdk-6-src-b16-24_apr_2009.tar.gz -// downloaded: 2009-05-07 - - -/* - * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -//package javax.imageio.spi; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.ServiceLoader; - -/** - * A registry for service provider instances. - * - *

A service is a well-known set of interfaces and (usually - * abstract) classes. A service provider is a specific - * implementation of a service. The classes in a provider typically - * implement the interface or subclass the class defined by the - * service itself. - * - *

Service providers are stored in one or more categories, - * each of which is defined by a class of interface (described by a - * Class object) that all of its members must implement. - * The set of categories may be changed dynamically. - * - *

Only a single instance of a given leaf class (that is, the - * actual class returned by getClass(), as opposed to any - * inherited classes or interfaces) may be registered. That is, - * suppose that the - * com.mycompany.mypkg.GreenServiceProvider class - * implements the com.mycompany.mypkg.MyService - * interface. If a GreenServiceProvider instance is - * registered, it will be stored in the category defined by the - * MyService class. If a new instance of - * GreenServiceProvider is registered, it will replace - * the previous instance. In practice, service provider objects are - * usually singletons so this behavior is appropriate. - * - *

To declare a service provider, a services - * subdirectory is placed within the META-INF directory - * that is present in every JAR file. This directory contains a file - * for each service provider interface that has one or more - * implementation classes present in the JAR file. For example, if - * the JAR file contained a class named - * com.mycompany.mypkg.MyServiceImpl which implements the - * javax.someapi.SomeService interface, the JAR file - * would contain a file named:

- * META-INF/services/javax.someapi.SomeService 
- * - * containing the line: - * - *
- * com.mycompany.mypkg.MyService
- * 
- * - *

The service provider classes should be to be lightweight and - * quick to load. Implementations of these interfaces should avoid - * complex dependencies on other classes and on native code. The usual - * pattern for more complex services is to register a lightweight - * proxy for the heavyweight service. - * - *

An application may customize the contents of a registry as it - * sees fit, so long as it has the appropriate runtime permission. - * - *

For more details on declaring service providers, and the JAR - * format in general, see the - * JAR File Specification. - * - * @see RegisterableService - * - */ -public class ServiceRegistry { - - // Class -> Registry - private Map, SubRegistry> categoryMap = new HashMap<>(); - - /** - * Constructs a ServiceRegistry instance with a - * set of categories taken from the categories - * argument. - * - * @param categories an Iterator containing - * Class objects to be used to define categories. - * - * @exception IllegalArgumentException if - * categories is null. - */ - public ServiceRegistry(Iterator> categories) { - if (categories == null) { - throw new IllegalArgumentException("categories == null!"); - } - while (categories.hasNext()) { - Class category = categories.next(); - SubRegistry reg = new SubRegistry(this, category); - categoryMap.put(category, reg); - } - } - - // The following two methods expose functionality from - // sun.misc.Service. If that class is made public, they may be - // removed. - // - // The sun.misc.ServiceConfigurationError class may also be - // exposed, in which case the references to 'an - // Error' below should be changed to 'a - // ServiceConfigurationError'. - - /** - * Searches for implementations of a particular service class - * using the given class loader. - * - *

This method transforms the name of the given service class - * into a provider-configuration filename as described in the - * class comment and then uses the getResources - * method of the given class loader to find all available files - * with that name. These files are then read and parsed to - * produce a list of provider-class names. The iterator that is - * returned uses the given class loader to look up and then - * instantiate each element of the list. - * - *

Because it is possible for extensions to be installed into - * a running Java virtual machine, this method may return - * different results each time it is invoked. - * - * @param providerClass a Classobject indicating the - * class or interface of the service providers being detected. - * - * @param loader the class loader to be used to load - * provider-configuration files and instantiate provider classes, - * or null if the system class loader (or, failing that - * the bootstrap class loader) is to be used. - * - * @return An Iterator that yields provider objects - * for the given service, in some arbitrary order. The iterator - * will throw an Error if a provider-configuration - * file violates the specified format or if a provider class - * cannot be found and instantiated. - * - * @exception IllegalArgumentException if - * providerClass is null. - */ - public static Iterator lookupProviders(Class providerClass, - ClassLoader loader) - { - if (providerClass == null) { - throw new IllegalArgumentException("providerClass == null!"); - } - return ServiceLoader.load(providerClass, loader).iterator(); - } - - /** - * Locates and incrementally instantiates the available providers - * of a given service using the context class loader. This - * convenience method is equivalent to: - * - *

-     *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
-     *   return Service.providers(service, cl);
-     * 
- * - * @param providerClass a Classobject indicating the - * class or interface of the service providers being detected. - * - * @return An Iterator that yields provider objects - * for the given service, in some arbitrary order. The iterator - * will throw an Error if a provider-configuration - * file violates the specified format or if a provider class - * cannot be found and instantiated. - * - * @exception IllegalArgumentException if - * providerClass is null. - */ - public static Iterator lookupProviders(Class providerClass) { - if (providerClass == null) { - throw new IllegalArgumentException("providerClass == null!"); - } - return ServiceLoader.load(providerClass).iterator(); - } - - /** - * Returns an Iterator of Class objects - * indicating the current set of categories. The iterator will be - * empty if no categories exist. - * - * @return an Iterator containing - * Classobjects. - */ - public Iterator> getCategories() { - Set> keySet = categoryMap.keySet(); - return keySet.iterator(); - } - - /** - * Returns an Iterator containing the subregistries to which the - * provider belongs. - */ - private Iterator getSubRegistries(Object provider) { - List l = new ArrayList<>(); - Iterator> iter = categoryMap.keySet().iterator(); - while (iter.hasNext()) { - Class c = iter.next(); - if (c.isAssignableFrom(provider.getClass())) { - l.add(categoryMap.get(c)); - } - } - return l.iterator(); - } - - /** - * Adds a service provider object to the registry. The provider - * is associated with the given category. - * - *

If provider implements the - * RegisterableService interface, its - * onRegistration method will be called. Its - * onDeregistration method will be called each time - * it is deregistered from a category, for example if a - * category is removed or the registry is garbage collected. - * - * @param provider the service provide object to be registered. - * @param category the category under which to register the - * provider. - * - * @return true if no provider of the same class was previously - * registered in the same category category. - * - * @exception IllegalArgumentException if provider is - * null. - * @exception IllegalArgumentException if there is no category - * corresponding to category. - * @exception ClassCastException if provider does not implement - * the Class defined by category. - */ - public boolean registerServiceProvider(T provider, - Class category) { - if (provider == null) { - throw new IllegalArgumentException("provider == null!"); - } - SubRegistry reg = categoryMap.get(category); - if (reg == null) { - throw new IllegalArgumentException("category unknown!"); - } - if (!category.isAssignableFrom(provider.getClass())) { - throw new ClassCastException(); - } - - return reg.registerServiceProvider(provider); - } - - /** - * Adds a service provider object to the registry. The provider - * is associated within each category present in the registry - * whose Class it implements. - * - *

If provider implements the - * RegisterableService interface, its - * onRegistration method will be called once for each - * category it is registered under. Its - * onDeregistration method will be called each time - * it is deregistered from a category or when the registry is - * finalized. - * - * @param provider the service provider object to be registered. - * - * @exception IllegalArgumentException if - * provider is null. - */ - public void registerServiceProvider(Object provider) { - if (provider == null) { - throw new IllegalArgumentException("provider == null!"); - } - Iterator regs = getSubRegistries(provider); - while (regs.hasNext()) { - SubRegistry reg = regs.next(); - reg.registerServiceProvider(provider); - } - } - - /** - * Adds a set of service provider objects, taken from an - * Iterator to the registry. Each provider is - * associated within each category present in the registry whose - * Class it implements. - * - *

For each entry of providers that implements - * the RegisterableService interface, its - * onRegistration method will be called once for each - * category it is registered under. Its - * onDeregistration method will be called each time - * it is deregistered from a category or when the registry is - * finalized. - * - * @param providers an Iterator containing service provider - * objects to be registered. - * - * @exception IllegalArgumentException if providers - * is null or contains a null entry. - */ - public void registerServiceProviders(Iterator providers) { - if (providers == null) { - throw new IllegalArgumentException("provider == null!"); - } - while (providers.hasNext()) { - registerServiceProvider(providers.next()); - } - } - - /** - * Removes a service provider object from the given category. If - * the provider was not previously registered, nothing happens and - * false is returned. Otherwise, true - * is returned. If an object of the same class as - * provider but not equal (using ==) to - * provider is registered, it will not be - * deregistered. - * - *

If provider implements the - * RegisterableService interface, its - * onDeregistration method will be called. - * - * @param provider the service provider object to be deregistered. - * @param category the category from which to deregister the - * provider. - * - * @return true if the provider was previously - * registered in the same category category, - * false otherwise. - * - * @exception IllegalArgumentException if provider is - * null. - * @exception IllegalArgumentException if there is no category - * corresponding to category. - * @exception ClassCastException if provider does not implement - * the class defined by category. - */ - public boolean deregisterServiceProvider(T provider, - Class category) { - if (provider == null) { - throw new IllegalArgumentException("provider == null!"); - } - SubRegistry reg = categoryMap.get(category); - if (reg == null) { - throw new IllegalArgumentException("category unknown!"); - } - if (!category.isAssignableFrom(provider.getClass())) { - throw new ClassCastException(); - } - return reg.deregisterServiceProvider(provider); - } - - /** - * Removes a service provider object from all categories that - * contain it. - * - * @param provider the service provider object to be deregistered. - * - * @exception IllegalArgumentException if provider is - * null. - */ - public void deregisterServiceProvider(Object provider) { - if (provider == null) { - throw new IllegalArgumentException("provider == null!"); - } - Iterator regs = getSubRegistries(provider); - while (regs.hasNext()) { - SubRegistry reg = regs.next(); - reg.deregisterServiceProvider(provider); - } - } - - /** - * Returns true if provider is currently - * registered. - * - * @param provider the service provider object to be queried. - * - * @return true if the given provider has been - * registered. - * - * @exception IllegalArgumentException if provider is - * null. - */ - public boolean contains(Object provider) { - if (provider == null) { - throw new IllegalArgumentException("provider == null!"); - } - Iterator regs = getSubRegistries(provider); - while (regs.hasNext()) { - SubRegistry reg = regs.next(); - if (reg.contains(provider)) { - return true; - } - } - - return false; - } - - /** - * Returns an Iterator containing all registered - * service providers in the given category. If - * useOrdering is false, the iterator - * will return all of the server provider objects in an arbitrary - * order. Otherwise, the ordering will respect any pairwise - * orderings that have been set. If the graph of pairwise - * orderings contains cycles, any providers that belong to a cycle - * will not be returned. - * - * @param category the category to be retrieved from. - * @param useOrdering true if pairwise orderings - * should be taken account in ordering the returned objects. - * - * @return an Iterator containing service provider - * objects from the given category, possibly in order. - * - * @exception IllegalArgumentException if there is no category - * corresponding to category. - */ - public Iterator getServiceProviders(Class category, - boolean useOrdering) { - SubRegistry reg = categoryMap.get(category); - if (reg == null) { - throw new IllegalArgumentException("category unknown!"); - } - return reg.getServiceProviders(useOrdering); - } - - /** - * A simple filter interface used by - * ServiceRegistry.getServiceProviders to select - * providers matching an arbitrary criterion. Classes that - * implement this interface should be defined in order to make use - * of the getServiceProviders method of - * ServiceRegistry that takes a Filter. - * - * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean) - */ - public interface Filter { - - /** - * Returns true if the given - * provider object matches the criterion defined - * by this Filter. - * - * @param provider a service provider Object. - * - * @return true if the provider matches the criterion. - */ - boolean filter(Object provider); - } - - /** - * Returns an Iterator containing service provider - * objects within a given category that satisfy a criterion - * imposed by the supplied ServiceRegistry.Filter - * object's filter method. - * - *

The useOrdering argument controls the - * ordering of the results using the same rules as - * getServiceProviders(Class, boolean). - * - * @param category the category to be retrieved from. - * @param filter an instance of ServiceRegistry.Filter - * whose filter method will be invoked. - * @param useOrdering true if pairwise orderings - * should be taken account in ordering the returned objects. - * - * @return an Iterator containing service provider - * objects from the given category, possibly in order. - * - * @exception IllegalArgumentException if there is no category - * corresponding to category. - */ - public Iterator getServiceProviders(Class category, - Filter filter, - boolean useOrdering) { - SubRegistry reg = categoryMap.get(category); - if (reg == null) { - throw new IllegalArgumentException("category unknown!"); - } - Iterator iter = getServiceProviders(category, useOrdering); - return new FilterIterator(iter, filter); - } - - /** - * Returns the currently registered service provider object that - * is of the given class type. At most one object of a given - * class is allowed to be registered at any given time. If no - * registered object has the desired class type, null - * is returned. - * - * @param providerClass the Class of the desired - * service provider object. - * - * @return a currently registered service provider object with the - * desired Classtype, or null is none is - * present. - * - * @exception IllegalArgumentException if providerClass is - * null. - */ - public T getServiceProviderByClass(Class providerClass) { - if (providerClass == null) { - throw new IllegalArgumentException("providerClass == null!"); - } - for (Class c : categoryMap.keySet()) { - if (c.isAssignableFrom(providerClass)) { - SubRegistry reg = (SubRegistry)categoryMap.get(c); - T provider = reg.getServiceProviderByClass(providerClass); - if (provider != null) { - return provider; - } - } - } - return null; - } - - /** - * Sets a pairwise ordering between two service provider objects - * within a given category. If one or both objects are not - * currently registered within the given category, or if the - * desired ordering is already set, nothing happens and - * false is returned. If the providers previously - * were ordered in the reverse direction, that ordering is - * removed. - * - *

The ordering will be used by the - * getServiceProviders methods when their - * useOrdering argument is true. - * - * @param category a Class object indicating the - * category under which the preference is to be established. - * @param firstProvider the preferred provider. - * @param secondProvider the provider to which - * firstProvider is preferred. - * - * @return true if a previously unset ordering - * was established. - * - * @exception IllegalArgumentException if either provider is - * null or they are the same object. - * @exception IllegalArgumentException if there is no category - * corresponding to category. - */ - public boolean setOrdering(Class category, - T firstProvider, - T secondProvider) { - if (firstProvider == null || secondProvider == null) { - throw new IllegalArgumentException("provider is null!"); - } - if (firstProvider == secondProvider) { - throw new IllegalArgumentException("providers are the same!"); - } - SubRegistry reg = (SubRegistry)categoryMap.get(category); - if (reg == null) { - throw new IllegalArgumentException("category unknown!"); - } - if (reg.contains(firstProvider) && - reg.contains(secondProvider)) { - return reg.setOrdering(firstProvider, secondProvider); - } - return false; - } - - /** - * Sets a pairwise ordering between two service provider objects - * within a given category. If one or both objects are not - * currently registered within the given category, or if no - * ordering is currently set between them, nothing happens - * and false is returned. - * - *

The ordering will be used by the - * getServiceProviders methods when their - * useOrdering argument is true. - * - * @param category a Class object indicating the - * category under which the preference is to be disestablished. - * @param firstProvider the formerly preferred provider. - * @param secondProvider the provider to which - * firstProvider was formerly preferred. - * - * @return true if a previously set ordering was - * disestablished. - * - * @exception IllegalArgumentException if either provider is - * null or they are the same object. - * @exception IllegalArgumentException if there is no category - * corresponding to category. - */ - public boolean unsetOrdering(Class category, - T firstProvider, - T secondProvider) { - if (firstProvider == null || secondProvider == null) { - throw new IllegalArgumentException("provider is null!"); - } - if (firstProvider == secondProvider) { - throw new IllegalArgumentException("providers are the same!"); - } - SubRegistry reg = (SubRegistry)categoryMap.get(category); - if (reg == null) { - throw new IllegalArgumentException("category unknown!"); - } - if (reg.contains(firstProvider) && - reg.contains(secondProvider)) { - return reg.unsetOrdering(firstProvider, secondProvider); - } - return false; - } - - /** - * Deregisters all service provider object currently registered - * under the given category. - * - * @param category the category to be emptied. - * - * @exception IllegalArgumentException if there is no category - * corresponding to category. - */ - public void deregisterAll(Class category) { - SubRegistry reg = (SubRegistry)categoryMap.get(category); - if (reg == null) { - throw new IllegalArgumentException("category unknown!"); - } - reg.clear(); - } - - /** - * Deregisters all currently registered service providers from all - * categories. - */ - public void deregisterAll() { - Iterator iter = categoryMap.values().iterator(); - while (iter.hasNext()) { - SubRegistry reg = (SubRegistry)iter.next(); - reg.clear(); - } - } - - /** - * Finalizes this object prior to garbage collection. The - * deregisterAll method is called to deregister all - * currently registered service providers. This method should not - * be called from application code. - * - * @exception Throwable if an error occurs during superclass - * finalization. - */ - public void finalize() throws Throwable { - deregisterAll(); - super.finalize(); - } -} - - -/** - * A portion of a registry dealing with a single superclass or - * interface. - */ -class SubRegistry { - - ServiceRegistry registry; - - Class category; - - // Provider Objects organized by partial oridering - PartiallyOrderedSet poset = new PartiallyOrderedSet(); - - // Class -> Provider Object of that class - Map,Object> map = new HashMap<>(); - - public SubRegistry(ServiceRegistry registry, Class category) { - this.registry = registry; - this.category = category; - } - - public boolean registerServiceProvider(Object provider) { - Object oprovider = map.get(provider.getClass()); - boolean present = oprovider != null; - - if (present) { - deregisterServiceProvider(oprovider); - } - map.put(provider.getClass(), provider); - poset.add(provider); - if (provider instanceof RegisterableService) { - RegisterableService rs = (RegisterableService)provider; - rs.onRegistration(registry, category); - } - - return !present; - } - - /** - * If the provider was not previously registered, do nothing. - * - * @return true if the provider was previously registered. - */ - public boolean deregisterServiceProvider(Object provider) { - Object oprovider = map.get(provider.getClass()); - - if (provider == oprovider) { - map.remove(provider.getClass()); - poset.remove(provider); - if (provider instanceof RegisterableService) { - RegisterableService rs = (RegisterableService)provider; - rs.onDeregistration(registry, category); - } - - return true; - } - return false; - } - - public boolean contains(Object provider) { - Object oprovider = map.get(provider.getClass()); - return oprovider == provider; - } - - public boolean setOrdering(Object firstProvider, - Object secondProvider) { - return poset.setOrdering(firstProvider, secondProvider); - } - - public boolean unsetOrdering(Object firstProvider, - Object secondProvider) { - return poset.unsetOrdering(firstProvider, secondProvider); - } - - public Iterator getServiceProviders(boolean useOrdering) { - if (useOrdering) { - return poset.iterator(); - } else { - return map.values().iterator(); - } - } - - public T getServiceProviderByClass(Class providerClass) { - return (T)map.get(providerClass); - } - - public void clear() { - Iterator iter = map.values().iterator(); - while (iter.hasNext()) { - Object provider = iter.next(); - iter.remove(); - - if (provider instanceof RegisterableService) { - RegisterableService rs = (RegisterableService)provider; - rs.onDeregistration(registry, category); - } - } - poset.clear(); - } - - public void finalize() { - clear(); - } -} - - -/** - * A class for wrapping Iterators with a filter function. - * This provides an iterator for a subset without duplication. - */ -class FilterIterator implements Iterator { - - private Iterator iter; - private ServiceRegistry.Filter filter; - - private T next = null; - - public FilterIterator(Iterator iter, - ServiceRegistry.Filter filter) { - this.iter = iter; - this.filter = filter; - advance(); - } - - private void advance() { - while (iter.hasNext()) { - T elt = iter.next(); - if (filter.filter(elt)) { - next = elt; - return; - } - } - - next = null; - } - - public boolean hasNext() { - return next != null; - } - - public T next() { - if (next == null) { - throw new NoSuchElementException(); - } - T o = next; - advance(); - return o; - } - - public void remove() { - throw new UnsupportedOperationException(); - } -} From a7b428862b42d0b69a0054fcb00ec40bcc6b51f8 Mon Sep 17 00:00:00 2001 From: Kim Jin Yeon Date: Mon, 3 Jul 2023 12:43:57 +0900 Subject: [PATCH 02/30] Add description on Basic File Aceess about FilePIDsEnabled --- doc/sphinx-guides/source/api/dataaccess.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/dataaccess.rst b/doc/sphinx-guides/source/api/dataaccess.rst index e76ea167587..98df2d5a046 100755 --- a/doc/sphinx-guides/source/api/dataaccess.rst +++ b/doc/sphinx-guides/source/api/dataaccess.rst @@ -83,7 +83,7 @@ Basic access URI: ``/api/access/datafile/$id`` -.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. +.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. (FilePIDsEnabled 옵션이 켜져 있을 때만 동작하는 것 언급하고 FilePIDsEnabled 설명 문서 링크 첨부하기) Example: Getting the file whose DOI is *10.5072/FK2/J8SJZB* :: From e3fd94502d4bc60541cd233847e92367d4be054d Mon Sep 17 00:00:00 2001 From: Minji Woo Date: Mon, 3 Jul 2023 13:05:52 +0900 Subject: [PATCH 03/30] Update dataaccess.rst file access General description of the basic file access: FilePIDsEnabled --- doc/sphinx-guides/source/api/dataaccess.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/dataaccess.rst b/doc/sphinx-guides/source/api/dataaccess.rst index 98df2d5a046..2ecdc0d2a23 100755 --- a/doc/sphinx-guides/source/api/dataaccess.rst +++ b/doc/sphinx-guides/source/api/dataaccess.rst @@ -83,7 +83,7 @@ Basic access URI: ``/api/access/datafile/$id`` -.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. (FilePIDsEnabled 옵션이 켜져 있을 때만 동작하는 것 언급하고 FilePIDsEnabled 설명 문서 링크 첨부하기) +.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. Example: Getting the file whose DOI is *10.5072/FK2/J8SJZB* :: From 89353d4fb8135b5f957cea799bc5347c6ae9c556 Mon Sep 17 00:00:00 2001 From: kmina02 <79454352+kmina02@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:12:14 +0900 Subject: [PATCH 04/30] Update dataaccess.rst put further link about FilePIDsEnabled --- doc/sphinx-guides/source/api/dataaccess.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/dataaccess.rst b/doc/sphinx-guides/source/api/dataaccess.rst index 2ecdc0d2a23..15a93ab436a 100755 --- a/doc/sphinx-guides/source/api/dataaccess.rst +++ b/doc/sphinx-guides/source/api/dataaccess.rst @@ -84,7 +84,7 @@ Basic access URI: ``/api/access/datafile/$id`` .. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. - +For further information, refer to https://guides.dataverse.org/en/5.13/installation/config.html?highlight=filepidsenabled#id255 Example: Getting the file whose DOI is *10.5072/FK2/J8SJZB* :: GET http://$SERVER/api/access/datafile/:persistentId?persistentId=doi:10.5072/FK2/J8SJZB From ca796d785b3470805c98adeb6ed86e843c5059b4 Mon Sep 17 00:00:00 2001 From: Kim Jin Yeon Date: Mon, 3 Jul 2023 13:22:48 +0900 Subject: [PATCH 05/30] Update dataaccess.rst --- doc/sphinx-guides/source/api/dataaccess.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/sphinx-guides/source/api/dataaccess.rst b/doc/sphinx-guides/source/api/dataaccess.rst index 15a93ab436a..e4cde143d29 100755 --- a/doc/sphinx-guides/source/api/dataaccess.rst +++ b/doc/sphinx-guides/source/api/dataaccess.rst @@ -83,8 +83,7 @@ Basic access URI: ``/api/access/datafile/$id`` -.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. -For further information, refer to https://guides.dataverse.org/en/5.13/installation/config.html?highlight=filepidsenabled#id255 +.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. For further information, refer to https://guides.dataverse.org/en/latest/installation/config.html?highlight=pidsenabled Example: Getting the file whose DOI is *10.5072/FK2/J8SJZB* :: GET http://$SERVER/api/access/datafile/:persistentId?persistentId=doi:10.5072/FK2/J8SJZB From 20e76599b78b5d77dee89784e99241b4095850b2 Mon Sep 17 00:00:00 2001 From: Kim Jin Yeon Date: Mon, 3 Jul 2023 14:06:03 +0900 Subject: [PATCH 06/30] Update dataaccess.rst --- doc/sphinx-guides/source/api/dataaccess.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/sphinx-guides/source/api/dataaccess.rst b/doc/sphinx-guides/source/api/dataaccess.rst index e4cde143d29..99e7f7eca92 100755 --- a/doc/sphinx-guides/source/api/dataaccess.rst +++ b/doc/sphinx-guides/source/api/dataaccess.rst @@ -84,6 +84,7 @@ Basic access URI: ``/api/access/datafile/$id`` .. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. For further information, refer to https://guides.dataverse.org/en/latest/installation/config.html?highlight=pidsenabled + Example: Getting the file whose DOI is *10.5072/FK2/J8SJZB* :: GET http://$SERVER/api/access/datafile/:persistentId?persistentId=doi:10.5072/FK2/J8SJZB From 17290efa53ac7b692a1d851b0d4e10a7d4cd8aea Mon Sep 17 00:00:00 2001 From: Kim Jin Yeon Date: Mon, 3 Jul 2023 14:12:33 +0900 Subject: [PATCH 07/30] Update dataaccess.rst --- doc/sphinx-guides/source/api/dataaccess.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/dataaccess.rst b/doc/sphinx-guides/source/api/dataaccess.rst index 99e7f7eca92..7b1feed7814 100755 --- a/doc/sphinx-guides/source/api/dataaccess.rst +++ b/doc/sphinx-guides/source/api/dataaccess.rst @@ -83,7 +83,7 @@ Basic access URI: ``/api/access/datafile/$id`` -.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. For further information, refer to https://guides.dataverse.org/en/latest/installation/config.html?highlight=pidsenabled +.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. For further information, refer to `FilePIDsEnabled `_ Example: Getting the file whose DOI is *10.5072/FK2/J8SJZB* :: From 7db5bcb9fff919c846f8494cd1f7fae98db0ddee Mon Sep 17 00:00:00 2001 From: sferey Date: Wed, 9 Aug 2023 17:20:13 +0200 Subject: [PATCH 08/30] exclusion of ingested files during mimetype redetection --- src/main/java/edu/harvard/iq/dataverse/api/Files.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index f6eda085c95..c915e4be5ec 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -726,6 +726,11 @@ public Response reingest(@Context ContainerRequestContext crc, @PathParam("id") public Response redetectDatafile(@Context ContainerRequestContext crc, @PathParam("id") String id, @QueryParam("dryRun") boolean dryRun) { try { DataFile dataFileIn = findDataFileOrDie(id); + // Ingested Files have mimetype = text/tab-separated-values + // No need to redetect + if (dataFileIn.isTabularData()) { + return error(Response.Status.BAD_REQUEST, "The file is an ingested tabular file."); + } String originalContentType = dataFileIn.getContentType(); DataFile dataFileOut = execCommand(new RedetectFileTypeCommand(createDataverseRequest(getRequestUser(crc)), dataFileIn, dryRun)); NullSafeJsonBuilder result = NullSafeJsonBuilder.jsonObjectBuilder() From 7dd17c732233ef63f808a0406675e2085cf0e666 Mon Sep 17 00:00:00 2001 From: Pradyumna Sridhara <95268690+prsridha@users.noreply.github.com> Date: Sat, 26 Aug 2023 20:21:29 +0530 Subject: [PATCH 09/30] Added additional line in Permalinks config Added an additional line to restart Payara after changing settings in Permalinks section --- doc/sphinx-guides/source/installation/config.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/sphinx-guides/source/installation/config.rst b/doc/sphinx-guides/source/installation/config.rst index f9fe74afc7c..c9779c6fb05 100644 --- a/doc/sphinx-guides/source/installation/config.rst +++ b/doc/sphinx-guides/source/installation/config.rst @@ -305,6 +305,8 @@ Here are the configuration options for PermaLinks: - :ref:`:DataFilePIDFormat <:DataFilePIDFormat>` (optional) - :ref:`:FilePIDsEnabled <:FilePIDsEnabled>` (optional, defaults to false) +You must restart Payara after making changes to these settings. + .. _auth-modes: Auth Modes: Local vs. Remote vs. Both From 2ec5d3651de13db4200f4428fc69eb2b8007602c Mon Sep 17 00:00:00 2001 From: GPortas Date: Wed, 6 Sep 2023 10:46:17 +0100 Subject: [PATCH 10/30] Added: getZipDownloadLimit and getEmbargoEnabled API info endpoints --- .../iq/dataverse/api/AbstractApiBean.java | 6 ++ .../edu/harvard/iq/dataverse/api/Info.java | 14 +++++ .../edu/harvard/iq/dataverse/api/InfoIT.java | 58 +++++++++++++++++-- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java b/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java index 5a4c9ab9058..0a0861fa1c9 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java @@ -708,6 +708,12 @@ protected Response ok( boolean value ) { .add("data", value).build() ).build(); } + protected Response ok(long value) { + return Response.ok().entity(Json.createObjectBuilder() + .add("status", ApiConstants.STATUS_OK) + .add("data", value).build()).build(); + } + /** * @param data Payload to return. * @param mediaType Non-JSON media type. diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Info.java b/src/main/java/edu/harvard/iq/dataverse/api/Info.java index 3349c34dfcc..be71a3a9fc7 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Info.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Info.java @@ -65,4 +65,18 @@ public Response getTermsOfUse(@Context ContainerRequestContext crc) { public Response getAllowsIncompleteMetadata() { return ok(JvmSettings.API_ALLOW_INCOMPLETE_METADATA.lookupOptional(Boolean.class).orElse(false)); } + + @GET + @Path("zipDownloadLimit") + public Response getZipDownloadLimit() { + long zipDownloadLimit = SystemConfig.getLongLimitFromStringOrDefault(settingsSvc.getValueForKey(SettingsServiceBean.Key.ZipDownloadLimit), SystemConfig.defaultZipDownloadLimit); + return ok(zipDownloadLimit); + } + + @GET + @Path("embargoEnabled") + public Response getEmbargoEnabled() { + String setting = settingsSvc.getValueForKey(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); + return ok(setting != null && !setting.equals("0")); + } } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java b/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java index 142b979ef3c..1b2c513252d 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java @@ -3,13 +3,27 @@ import static io.restassured.RestAssured.given; import io.restassured.response.Response; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; +import static jakarta.ws.rs.core.Response.Status.OK; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; public class InfoIT { + @BeforeAll + public static void setUpClass() { + UtilIT.deleteSetting(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); + } + + @AfterAll + public static void afterClass() { + UtilIT.deleteSetting(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); + } + @Test public void testGetDatasetPublishPopupCustomText() { @@ -21,7 +35,7 @@ public void testGetDatasetPublishPopupCustomText() { Response response = given().urlEncodingEnabled(false) .get("/api/info/settings/" + SettingsServiceBean.Key.DatasetPublishPopupCustomText); response.prettyPrint(); - response.then().assertThat().statusCode(200) + response.then().assertThat().statusCode(OK.getStatusCode()) .body("data.message", equalTo("Hello world!")); given().urlEncodingEnabled(false) @@ -31,7 +45,7 @@ public void testGetDatasetPublishPopupCustomText() { response = given().urlEncodingEnabled(false) .get("/api/info/settings/" + SettingsServiceBean.Key.DatasetPublishPopupCustomText); response.prettyPrint(); - response.then().assertThat().statusCode(404) + response.then().assertThat().statusCode(NOT_FOUND.getStatusCode()) .body("message", equalTo("Setting " + SettingsServiceBean.Key.DatasetPublishPopupCustomText + " not found")); @@ -42,7 +56,7 @@ public void testGetVersion() { Response response = given().urlEncodingEnabled(false) .get("/api/info/version"); response.prettyPrint(); - response.then().assertThat().statusCode(200) + response.then().assertThat().statusCode(OK.getStatusCode()) .body("data.version", notNullValue()); } @@ -51,7 +65,7 @@ public void testGetServer() { Response response = given().urlEncodingEnabled(false) .get("/api/info/server"); response.prettyPrint(); - response.then().assertThat().statusCode(200) + response.then().assertThat().statusCode(OK.getStatusCode()) .body("data.message", notNullValue()); } @@ -60,7 +74,41 @@ public void getTermsOfUse() { Response response = given().urlEncodingEnabled(false) .get("/api/info/apiTermsOfUse"); response.prettyPrint(); - response.then().assertThat().statusCode(200) + response.then().assertThat().statusCode(OK.getStatusCode()) .body("data.message", notNullValue()); } + + @Test + public void getAllowsIncompleteMetadata() { + Response response = given().urlEncodingEnabled(false) + .get("/api/info/settings/incompleteMetadataViaApi"); + response.prettyPrint(); + response.then().assertThat().statusCode(OK.getStatusCode()) + .body("data", notNullValue()); + } + + @Test + public void getZipDownloadLimit() { + Response response = given().urlEncodingEnabled(false) + .get("/api/info/zipDownloadLimit"); + response.prettyPrint(); + response.then().assertThat().statusCode(OK.getStatusCode()) + .body("data", notNullValue()); + } + + @Test + public void getEmbargoEnabled() { + String testEndpoint = "/api/info/embargoEnabled"; + // Embargo disabled + Response response = given().urlEncodingEnabled(false).get(testEndpoint); + response.prettyPrint(); + response.then().assertThat().statusCode(OK.getStatusCode()) + .body("data", equalTo(false)); + // Embargo enabled + UtilIT.setSetting(SettingsServiceBean.Key.MaxEmbargoDurationInMonths, "12"); + response = given().urlEncodingEnabled(false).get(testEndpoint); + response.prettyPrint(); + response.then().assertThat().statusCode(OK.getStatusCode()) + .body("data", equalTo(true)); + } } From 7dd6ca8c76202a4a38d443e77e56ccf20548054f Mon Sep 17 00:00:00 2001 From: GPortas Date: Wed, 6 Sep 2023 12:25:21 +0100 Subject: [PATCH 11/30] Added: docs for new info API endpoints --- .../9880-info-api-zip-limit-embargo.md | 5 +++ doc/sphinx-guides/source/api/native-api.rst | 37 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 doc/release-notes/9880-info-api-zip-limit-embargo.md diff --git a/doc/release-notes/9880-info-api-zip-limit-embargo.md b/doc/release-notes/9880-info-api-zip-limit-embargo.md new file mode 100644 index 00000000000..6e15d503ae9 --- /dev/null +++ b/doc/release-notes/9880-info-api-zip-limit-embargo.md @@ -0,0 +1,5 @@ +Implemented the following new endpoints: + +- getZipDownloadLimit (/api/info/zipDownloadLimit): Get the configured zip file download limit. The response contains the long value of the limit in bytes. + +- getEmbargoEnabled (/api/info/embargoEnabled): Know if the Dataverse instance has been configured to allow embargoes. diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 4d9466703e4..44486278cc8 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -3330,6 +3330,43 @@ The fully expanded example above (without environment variables) looks like this curl "https://demo.dataverse.org/api/info/settings/incompleteMetadataViaApi" +Get Zip File Download Limit +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Get the configured zip file download limit. The response contains the long value of the limit in bytes. + +This limit comes from the database setting :ref:`:ZipDownloadLimit` if set, or the default value if the database setting is not set, which is 104857600 (100MB). + +.. code-block:: bash + + export SERVER_URL=https://demo.dataverse.org + + curl "$SERVER_URL/api/info/zipDownloadLimit" + +The fully expanded example above (without environment variables) looks like this: + +.. code-block:: bash + + curl "https://demo.dataverse.org/api/info/zipDownloadLimit" + +Show Support Of The Embargo Feature +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Know if the Dataverse instance has been configured to allow embargoes. + +The endpoint checks whether the database setting :ref:`:MaxEmbargoDurationInMonths`, which enables the embargo feature, has a value that enables the feature or not. + +.. code-block:: bash + + export SERVER_URL=https://demo.dataverse.org + + curl "$SERVER_URL/api/info/embargoEnabled" + +The fully expanded example above (without environment variables) looks like this: + +.. code-block:: bash + + curl "https://demo.dataverse.org/api/info/embargoEnabled" .. _metadata-blocks-api: From e0bac9c26113da3296dffb830dbf1384bc60cc91 Mon Sep 17 00:00:00 2001 From: GPortas Date: Wed, 6 Sep 2023 12:33:42 +0100 Subject: [PATCH 12/30] Fixed: missing guides reference in config.rst --- doc/sphinx-guides/source/installation/config.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/sphinx-guides/source/installation/config.rst b/doc/sphinx-guides/source/installation/config.rst index f9fe74afc7c..9fc7142333f 100644 --- a/doc/sphinx-guides/source/installation/config.rst +++ b/doc/sphinx-guides/source/installation/config.rst @@ -2980,6 +2980,8 @@ This setting controls the number of files that can be uploaded through the UI at ``curl -X PUT -d 500 http://localhost:8080/api/admin/settings/:MultipleUploadFilesLimit`` +.. _:ZipDownloadLimit: + :ZipDownloadLimit +++++++++++++++++ From 3bcc5557347b20625f7264ed7d760e03e69a8414 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 6 Sep 2023 16:12:48 -0400 Subject: [PATCH 13/30] fix command for only building configbaker image As discussed here: https://dataverse.zulipchat.com/#narrow/stream/375812-containers/topic/iterating.20on.20configbaker --- doc/sphinx-guides/source/container/configbaker-image.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/container/configbaker-image.rst b/doc/sphinx-guides/source/container/configbaker-image.rst index 7218e2d8d14..d098bd46436 100644 --- a/doc/sphinx-guides/source/container/configbaker-image.rst +++ b/doc/sphinx-guides/source/container/configbaker-image.rst @@ -86,7 +86,7 @@ Maven modules packaging target with activated "container" profile from the proje If you specifically want to build a config baker image *only*, try -``mvn -Pct package -Ddocker.filter=dev_bootstrap`` +``mvn -Pct docker:build -Ddocker.filter=dev_bootstrap`` The build of config baker involves copying Solr configset files. The Solr version used is inherited from Maven, acting as the single source of truth. Also, the tag of the image should correspond the application image, as From 71aa91c1c9762c08efd189e8eecf4b650db25424 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Wed, 6 Sep 2023 16:59:55 -0400 Subject: [PATCH 14/30] increase configbaker timeout Based on discussion here: https://dataverse.zulipchat.com/#narrow/stream/375812-containers/topic/running.20using.20compose --- modules/container-configbaker/scripts/bootstrap.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/container-configbaker/scripts/bootstrap.sh b/modules/container-configbaker/scripts/bootstrap.sh index 1aa9e232953..f9f97e85a5a 100644 --- a/modules/container-configbaker/scripts/bootstrap.sh +++ b/modules/container-configbaker/scripts/bootstrap.sh @@ -13,7 +13,7 @@ function usage() { echo "" echo "Parameters:" echo "instanceUrl - Location on container network where to reach your instance. Default: 'http://dataverse:8080'" - echo " timeout - Provide how long to wait for the instance to become available (using wait4x). Default: '2m'" + echo " timeout - Provide how long to wait for the instance to become available (using wait4x). Default: '3m'" echo " persona - Configure persona to execute. Calls ${BOOTSTRAP_DIR}//init.sh. Default: 'base'" echo "" echo "Note: This script will wait for the Dataverse instance to be available before executing the bootstrapping." @@ -24,7 +24,7 @@ function usage() { # Set some defaults as documented DATAVERSE_URL=${DATAVERSE_URL:-"http://dataverse:8080"} -TIMEOUT=${TIMEOUT:-"2m"} +TIMEOUT=${TIMEOUT:-"3m"} while getopts "u:t:h" OPTION do From 75b683216b1c602cd114776bbff5e78a94ed402a Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Thu, 7 Sep 2023 16:32:32 -0400 Subject: [PATCH 15/30] stub out page on API design, esp paths #9880 --- .../source/developers/api-design.rst | 71 +++++++++++++++++++ doc/sphinx-guides/source/developers/index.rst | 1 + 2 files changed, 72 insertions(+) create mode 100755 doc/sphinx-guides/source/developers/api-design.rst diff --git a/doc/sphinx-guides/source/developers/api-design.rst b/doc/sphinx-guides/source/developers/api-design.rst new file mode 100755 index 00000000000..e485236223e --- /dev/null +++ b/doc/sphinx-guides/source/developers/api-design.rst @@ -0,0 +1,71 @@ +========== +API Design +========== + +API design is a large topic. We expect this page to grow over time. + +.. contents:: |toctitle| + :local: + +Paths +----- + +A reminder `from Wikipedia `_ of what a path is: + +.. code-block:: bash + + userinfo host port + ┌──┴───┐ ┌──────┴──────┐ ┌┴┐ + https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top + └─┬─┘ └─────────────┬────────────┘└───────┬───────┘ └────────────┬────────────┘ └┬┘ + scheme authority path query fragment + +Exposing Settings +~~~~~~~~~~~~~~~~~ + +Since Dataverse 4, database settings have been exposed via API at http://localhost:8080/api/admin/settings + +(JVM options are probably available via the Payara REST API, but this is out of scope.) + +Settings need to be exposed outside to API clients outside of ``/api/admin`` (which is typically restricted to localhost). Here are some guidelines to follow when exposing settings. + +- When you are exposing a database setting as-is: + + - Use ``/api/info/settings`` as the root path. + + - Append the name of the setting including the colon (e.g. ``:DatasetPublishPopupCustomText``) + + - Final path example: ``/api/info/settings/:DatasetPublishPopupCustomText`` + +- If the absence of the database setting is filled in by a default value (e.g. ``:ZipDownloadLimit`` or ``:ApiTermsOfUse``): + + - Use ``/api/info`` as the root path. + + - Append the setting but remove the colon and downcase the first character (e.g. ``zipDownloadLimit``) + + - Final path example: ``/api/info/zipDownloadLimit`` + +- If you are exposing logic based on a database setting rather that the setting itself (e.g. a boolean for if embargo is enabled rather than the value of ``:MaxEmbargoDurationInMonths``): + + - Use ``/api/info`` as the root path. + + - Append a meaningful name for the setting (e.g. ``embargoEnabled``). + + - Final path example: ``/api/info/embargoEnabled`` + +- If the database setting you're exposing make more sense outside of ``/api/info`` because there's more context (e.g. ``:CustomDatasetSummaryFields``): + + - Feel free to use a path outside of ``/api/info`` as the root path. + + - Given additional context, append a shortened name (e.g. ``/api/datasets/summaryFieldNames``). + + - Final path example: ``/api/datasets/summaryFieldNames`` + +- If you need to expose a JVM option (MicroProfile setting) such as ``dataverse.api.allow-incomplete-metadata``: + + - Use ``/api/info`` as the root path. + + - Append a meaningful name for the setting (e.g. ``incompleteMetadataViaApi``). + + - Final path example: ``/api/info/embargoEnabled`` + diff --git a/doc/sphinx-guides/source/developers/index.rst b/doc/sphinx-guides/source/developers/index.rst index c77ddc13519..977aeb79697 100755 --- a/doc/sphinx-guides/source/developers/index.rst +++ b/doc/sphinx-guides/source/developers/index.rst @@ -19,6 +19,7 @@ Developer Guide sql-upgrade-scripts testing documentation + api-design security dependencies debugging From 972645153d1a0401196b18e1eacdc2c41b193e49 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 11 Sep 2023 12:10:23 +0100 Subject: [PATCH 16/30] Changed: :MaxEmbargoDurationInMonths setting directly exposed via API info endpoint --- doc/sphinx-guides/source/api/native-api.rst | 16 +++-- .../edu/harvard/iq/dataverse/api/Info.java | 35 ++++++----- .../edu/harvard/iq/dataverse/api/InfoIT.java | 59 +++++++------------ 3 files changed, 51 insertions(+), 59 deletions(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 44486278cc8..52d9099cf63 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -3318,6 +3318,8 @@ Show Support Of Incomplete Metadata Deposition Learn if an instance has been configured to allow deposition of incomplete datasets via the API. See also :ref:`create-dataset-command` and :ref:`dataverse.api.allow-incomplete-metadata` +.. note:: See :ref:`curl-examples-and-environment-variables` if you are unfamiliar with the use of export below. + .. code-block:: bash export SERVER_URL=https://demo.dataverse.org @@ -3337,6 +3339,8 @@ Get the configured zip file download limit. The response contains the long value This limit comes from the database setting :ref:`:ZipDownloadLimit` if set, or the default value if the database setting is not set, which is 104857600 (100MB). +.. note:: See :ref:`curl-examples-and-environment-variables` if you are unfamiliar with the use of export below. + .. code-block:: bash export SERVER_URL=https://demo.dataverse.org @@ -3349,24 +3353,24 @@ The fully expanded example above (without environment variables) looks like this curl "https://demo.dataverse.org/api/info/zipDownloadLimit" -Show Support Of The Embargo Feature -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Get Maximum Embargo Duration In Months +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Know if the Dataverse instance has been configured to allow embargoes. +Get the maximum embargo duration in months, if available, configured through the database setting :ref:`:MaxEmbargoDurationInMonths` from the Configuration section of the Installation Guide. -The endpoint checks whether the database setting :ref:`:MaxEmbargoDurationInMonths`, which enables the embargo feature, has a value that enables the feature or not. +.. note:: See :ref:`curl-examples-and-environment-variables` if you are unfamiliar with the use of export below. .. code-block:: bash export SERVER_URL=https://demo.dataverse.org - curl "$SERVER_URL/api/info/embargoEnabled" + curl "$SERVER_URL/api/info/settings/:MaxEmbargoDurationInMonths" The fully expanded example above (without environment variables) looks like this: .. code-block:: bash - curl "https://demo.dataverse.org/api/info/embargoEnabled" + curl "https://demo.dataverse.org/api/info/settings/:MaxEmbargoDurationInMonths" .. _metadata-blocks-api: diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Info.java b/src/main/java/edu/harvard/iq/dataverse/api/Info.java index be71a3a9fc7..ccf918f1104 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Info.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Info.java @@ -25,14 +25,15 @@ public class Info extends AbstractApiBean { @GET @Path("settings/:DatasetPublishPopupCustomText") public Response getDatasetPublishPopupCustomText() { - String setting = settingsService.getValueForKey(SettingsServiceBean.Key.DatasetPublishPopupCustomText); - if (setting != null) { - return ok(Json.createObjectBuilder().add("message", setting)); - } else { - return notFound("Setting " + SettingsServiceBean.Key.DatasetPublishPopupCustomText + " not found"); - } + return getSettingByKey(SettingsServiceBean.Key.DatasetPublishPopupCustomText); } - + + @GET + @Path("settings/:MaxEmbargoDurationInMonths") + public Response getMaxEmbargoDurationInMonths() { + return getSettingByKey(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); + } + @GET @AuthRequired @Path("version") @@ -41,25 +42,25 @@ public Response getInfo(@Context ContainerRequestContext crc) { String[] comps = versionStr.split("build",2); String version = comps[0].trim(); JsonValue build = comps.length > 1 ? Json.createArrayBuilder().add(comps[1].trim()).build().get(0) : JsonValue.NULL; - + return response( req -> ok( Json.createObjectBuilder().add("version", version) .add("build", build)), getRequestUser(crc)); } - + @GET @AuthRequired @Path("server") public Response getServer(@Context ContainerRequestContext crc) { return response( req -> ok(JvmSettings.FQDN.lookup()), getRequestUser(crc)); } - + @GET @AuthRequired @Path("apiTermsOfUse") public Response getTermsOfUse(@Context ContainerRequestContext crc) { return response( req -> ok(systemConfig.getApiTermsOfUse()), getRequestUser(crc)); } - + @GET @Path("settings/incompleteMetadataViaApi") public Response getAllowsIncompleteMetadata() { @@ -73,10 +74,12 @@ public Response getZipDownloadLimit() { return ok(zipDownloadLimit); } - @GET - @Path("embargoEnabled") - public Response getEmbargoEnabled() { - String setting = settingsSvc.getValueForKey(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); - return ok(setting != null && !setting.equals("0")); + private Response getSettingByKey(SettingsServiceBean.Key key) { + String setting = settingsService.getValueForKey(key); + if (setting != null) { + return ok(Json.createObjectBuilder().add("message", setting)); + } else { + return notFound("Setting " + key + " not found"); + } } } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java b/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java index 1b2c513252d..3d5691dbe03 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/InfoIT.java @@ -1,6 +1,7 @@ package edu.harvard.iq.dataverse.api; import static io.restassured.RestAssured.given; + import io.restassured.response.Response; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; import org.junit.jupiter.api.AfterAll; @@ -17,38 +18,23 @@ public class InfoIT { @BeforeAll public static void setUpClass() { UtilIT.deleteSetting(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); + UtilIT.deleteSetting(SettingsServiceBean.Key.DatasetPublishPopupCustomText); } @AfterAll public static void afterClass() { UtilIT.deleteSetting(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); + UtilIT.deleteSetting(SettingsServiceBean.Key.DatasetPublishPopupCustomText); } @Test public void testGetDatasetPublishPopupCustomText() { + testSettingEndpoint(SettingsServiceBean.Key.DatasetPublishPopupCustomText, "Hello world!"); + } - given().urlEncodingEnabled(false) - .body("Hello world!") - .put("/api/admin/settings/" - + SettingsServiceBean.Key.DatasetPublishPopupCustomText); - - Response response = given().urlEncodingEnabled(false) - .get("/api/info/settings/" + SettingsServiceBean.Key.DatasetPublishPopupCustomText); - response.prettyPrint(); - response.then().assertThat().statusCode(OK.getStatusCode()) - .body("data.message", equalTo("Hello world!")); - - given().urlEncodingEnabled(false) - .delete("/api/admin/settings/" - + SettingsServiceBean.Key.DatasetPublishPopupCustomText); - - response = given().urlEncodingEnabled(false) - .get("/api/info/settings/" + SettingsServiceBean.Key.DatasetPublishPopupCustomText); - response.prettyPrint(); - response.then().assertThat().statusCode(NOT_FOUND.getStatusCode()) - .body("message", equalTo("Setting " - + SettingsServiceBean.Key.DatasetPublishPopupCustomText - + " not found")); + @Test + public void testGetMaxEmbargoDurationInMonths() { + testSettingEndpoint(SettingsServiceBean.Key.MaxEmbargoDurationInMonths, "12"); } @Test @@ -68,9 +54,9 @@ public void testGetServer() { response.then().assertThat().statusCode(OK.getStatusCode()) .body("data.message", notNullValue()); } - + @Test - public void getTermsOfUse() { + public void testGetTermsOfUse() { Response response = given().urlEncodingEnabled(false) .get("/api/info/apiTermsOfUse"); response.prettyPrint(); @@ -79,7 +65,7 @@ public void getTermsOfUse() { } @Test - public void getAllowsIncompleteMetadata() { + public void testGetAllowsIncompleteMetadata() { Response response = given().urlEncodingEnabled(false) .get("/api/info/settings/incompleteMetadataViaApi"); response.prettyPrint(); @@ -88,7 +74,7 @@ public void getAllowsIncompleteMetadata() { } @Test - public void getZipDownloadLimit() { + public void testGetZipDownloadLimit() { Response response = given().urlEncodingEnabled(false) .get("/api/info/zipDownloadLimit"); response.prettyPrint(); @@ -96,19 +82,18 @@ public void getZipDownloadLimit() { .body("data", notNullValue()); } - @Test - public void getEmbargoEnabled() { - String testEndpoint = "/api/info/embargoEnabled"; - // Embargo disabled - Response response = given().urlEncodingEnabled(false).get(testEndpoint); + private void testSettingEndpoint(SettingsServiceBean.Key settingKey, String testSettingValue) { + String endpoint = "/api/info/settings/" + settingKey; + // Setting not found + Response response = given().urlEncodingEnabled(false).get(endpoint); response.prettyPrint(); - response.then().assertThat().statusCode(OK.getStatusCode()) - .body("data", equalTo(false)); - // Embargo enabled - UtilIT.setSetting(SettingsServiceBean.Key.MaxEmbargoDurationInMonths, "12"); - response = given().urlEncodingEnabled(false).get(testEndpoint); + response.then().assertThat().statusCode(NOT_FOUND.getStatusCode()) + .body("message", equalTo("Setting " + settingKey + " not found")); + // Setting exists + UtilIT.setSetting(settingKey, testSettingValue); + response = given().urlEncodingEnabled(false).get(endpoint); response.prettyPrint(); response.then().assertThat().statusCode(OK.getStatusCode()) - .body("data", equalTo(true)); + .body("data.message", equalTo(testSettingValue)); } } From d3d305c78ac579513654eb4c8a4add6d1ff8db88 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 11 Sep 2023 12:12:31 +0100 Subject: [PATCH 17/30] Changed: updated release notes --- doc/release-notes/9880-info-api-zip-limit-embargo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release-notes/9880-info-api-zip-limit-embargo.md b/doc/release-notes/9880-info-api-zip-limit-embargo.md index 6e15d503ae9..d2afb139e72 100644 --- a/doc/release-notes/9880-info-api-zip-limit-embargo.md +++ b/doc/release-notes/9880-info-api-zip-limit-embargo.md @@ -2,4 +2,4 @@ Implemented the following new endpoints: - getZipDownloadLimit (/api/info/zipDownloadLimit): Get the configured zip file download limit. The response contains the long value of the limit in bytes. -- getEmbargoEnabled (/api/info/embargoEnabled): Know if the Dataverse instance has been configured to allow embargoes. +- getMaxEmbargoDurationInMonths (/api/info/settings/:MaxEmbargoDurationInMonths): Get the maximum embargo duration in months, if available, configured through the database setting :MaxEmbargoDurationInMonths. From f754b710934dc3412614ff77cde8a4c2c2efafd2 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 11 Sep 2023 12:15:02 +0100 Subject: [PATCH 18/30] Changed: private Info.java method renamed --- src/main/java/edu/harvard/iq/dataverse/api/Info.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Info.java b/src/main/java/edu/harvard/iq/dataverse/api/Info.java index ccf918f1104..0652539b595 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Info.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Info.java @@ -25,13 +25,13 @@ public class Info extends AbstractApiBean { @GET @Path("settings/:DatasetPublishPopupCustomText") public Response getDatasetPublishPopupCustomText() { - return getSettingByKey(SettingsServiceBean.Key.DatasetPublishPopupCustomText); + return getSettingResponseByKey(SettingsServiceBean.Key.DatasetPublishPopupCustomText); } @GET @Path("settings/:MaxEmbargoDurationInMonths") public Response getMaxEmbargoDurationInMonths() { - return getSettingByKey(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); + return getSettingResponseByKey(SettingsServiceBean.Key.MaxEmbargoDurationInMonths); } @GET @@ -74,7 +74,7 @@ public Response getZipDownloadLimit() { return ok(zipDownloadLimit); } - private Response getSettingByKey(SettingsServiceBean.Key key) { + private Response getSettingResponseByKey(SettingsServiceBean.Key key) { String setting = settingsService.getValueForKey(key); if (setting != null) { return ok(Json.createObjectBuilder().add("message", setting)); From 7929c4ffae6bb3d0d9662a7d06b2e50e7bafdcb6 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Mon, 11 Sep 2023 10:02:28 -0400 Subject: [PATCH 19/30] remove embargo example, no longer used in #9881 --- doc/sphinx-guides/source/developers/api-design.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/doc/sphinx-guides/source/developers/api-design.rst b/doc/sphinx-guides/source/developers/api-design.rst index e485236223e..d7a14716c4d 100755 --- a/doc/sphinx-guides/source/developers/api-design.rst +++ b/doc/sphinx-guides/source/developers/api-design.rst @@ -45,14 +45,6 @@ Settings need to be exposed outside to API clients outside of ``/api/admin`` (wh - Final path example: ``/api/info/zipDownloadLimit`` -- If you are exposing logic based on a database setting rather that the setting itself (e.g. a boolean for if embargo is enabled rather than the value of ``:MaxEmbargoDurationInMonths``): - - - Use ``/api/info`` as the root path. - - - Append a meaningful name for the setting (e.g. ``embargoEnabled``). - - - Final path example: ``/api/info/embargoEnabled`` - - If the database setting you're exposing make more sense outside of ``/api/info`` because there's more context (e.g. ``:CustomDatasetSummaryFields``): - Feel free to use a path outside of ``/api/info`` as the root path. From 6e08bad0eb3e979fd50e7cbac7cfab4e2b2d5cd5 Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Mon, 11 Sep 2023 11:23:11 -0400 Subject: [PATCH 20/30] typo #9880 --- doc/sphinx-guides/source/developers/api-design.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/developers/api-design.rst b/doc/sphinx-guides/source/developers/api-design.rst index d7a14716c4d..e7a7a6408bb 100755 --- a/doc/sphinx-guides/source/developers/api-design.rst +++ b/doc/sphinx-guides/source/developers/api-design.rst @@ -59,5 +59,5 @@ Settings need to be exposed outside to API clients outside of ``/api/admin`` (wh - Append a meaningful name for the setting (e.g. ``incompleteMetadataViaApi``). - - Final path example: ``/api/info/embargoEnabled`` + - Final path example: ``/api/info/incompleteMetadataViaApi`` From 918f1fb90a6ebc6072f91a62a54223bae3ad132a Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 12 Sep 2023 09:31:10 -0400 Subject: [PATCH 21/30] Revert "#9717 grant CREATE instead of ALL per pdurbin" This reverts commit f71274e7c7a4d47ab7fb973320bcfdb7e6822fbd. --- scripts/installer/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/installer/install.py b/scripts/installer/install.py index 3aedbd8c6ad..9da64bff32e 100644 --- a/scripts/installer/install.py +++ b/scripts/installer/install.py @@ -413,7 +413,7 @@ # 3e. set permissions: - conn_cmd = "GRANT CREATE PRIVILEGES on DATABASE "+pgDb+" to "+pgUser+";" + conn_cmd = "GRANT ALL PRIVILEGES on DATABASE "+pgDb+" to "+pgUser+";" try: cur.execute(conn_cmd) except: From d6727c0bb7409a195fbb1878a492f5c2b0b23d4d Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Tue, 12 Sep 2023 10:54:54 -0400 Subject: [PATCH 22/30] CREATE instead of ALL for public schema --- scripts/installer/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/installer/install.py b/scripts/installer/install.py index 9da64bff32e..5a7b9f75696 100644 --- a/scripts/installer/install.py +++ b/scripts/installer/install.py @@ -422,7 +422,7 @@ conn.close() if int(pg_major_version) >= 15: - conn_cmd = "GRANT ALL ON SCHEMA public TO "+pgUser+";" + conn_cmd = "GRANT CREATE ON SCHEMA public TO "+pgUser+";" print("PostgreSQL 15 or higher detected. Running " + conn_cmd) try: cur.execute(conn_cmd) From f1df8290df9452a307f4bc8941affb10bafb87e8 Mon Sep 17 00:00:00 2001 From: Don Sizemore Date: Mon, 18 Sep 2023 15:37:25 -0400 Subject: [PATCH 23/30] #9760 add UBC Dataverse_Utils to client libraries page --- doc/sphinx-guides/source/api/client-libraries.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/sphinx-guides/source/api/client-libraries.rst b/doc/sphinx-guides/source/api/client-libraries.rst index 62069f62c23..b79996d3df4 100755 --- a/doc/sphinx-guides/source/api/client-libraries.rst +++ b/doc/sphinx-guides/source/api/client-libraries.rst @@ -50,6 +50,8 @@ Python There are multiple Python modules for interacting with Dataverse APIs. +`UBC's Dataverse Utilities `_ are a set of Python console utilities which allow one to upload datasets from a tab-separated-value spreadsheet, bulk release multiple datasets, bulk delete unpublished datasets, quickly duplicate records. replace licenses, and more. For additional information see their `PyPi page `_. + `EasyDataverse `_ is a Python library designed to simplify the management of Dataverse datasets in an object-oriented way, giving users the ability to upload, download, and update datasets with ease. By utilizing metadata block configurations, EasyDataverse automatically generates Python objects that contain all the necessary details required to create the native Dataverse JSON format used to create or edit datasets. Adding files and directories is also possible with EasyDataverse and requires no additional API calls. This library is particularly well-suited for client applications such as workflows and scripts as it minimizes technical complexities and facilitates swift development. `pyDataverse `_ primarily allows developers to manage Dataverse collections, datasets and datafiles. Its intention is to help with data migrations and DevOps activities such as testing and configuration management. The module is developed by `Stefan Kasberger `_ from `AUSSDA - The Austrian Social Science Data Archive `_. From a1a1233d2dbaa09b30e306960187bf3b29fc1337 Mon Sep 17 00:00:00 2001 From: Don Sizemore Date: Mon, 18 Sep 2023 15:46:43 -0400 Subject: [PATCH 24/30] #9760 move UBC beneath pyDataverse per pdurbin --- doc/sphinx-guides/source/api/client-libraries.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sphinx-guides/source/api/client-libraries.rst b/doc/sphinx-guides/source/api/client-libraries.rst index b79996d3df4..a25efe3a5f8 100755 --- a/doc/sphinx-guides/source/api/client-libraries.rst +++ b/doc/sphinx-guides/source/api/client-libraries.rst @@ -50,12 +50,12 @@ Python There are multiple Python modules for interacting with Dataverse APIs. -`UBC's Dataverse Utilities `_ are a set of Python console utilities which allow one to upload datasets from a tab-separated-value spreadsheet, bulk release multiple datasets, bulk delete unpublished datasets, quickly duplicate records. replace licenses, and more. For additional information see their `PyPi page `_. - `EasyDataverse `_ is a Python library designed to simplify the management of Dataverse datasets in an object-oriented way, giving users the ability to upload, download, and update datasets with ease. By utilizing metadata block configurations, EasyDataverse automatically generates Python objects that contain all the necessary details required to create the native Dataverse JSON format used to create or edit datasets. Adding files and directories is also possible with EasyDataverse and requires no additional API calls. This library is particularly well-suited for client applications such as workflows and scripts as it minimizes technical complexities and facilitates swift development. `pyDataverse `_ primarily allows developers to manage Dataverse collections, datasets and datafiles. Its intention is to help with data migrations and DevOps activities such as testing and configuration management. The module is developed by `Stefan Kasberger `_ from `AUSSDA - The Austrian Social Science Data Archive `_. +`UBC's Dataverse Utilities `_ are a set of Python console utilities which allow one to upload datasets from a tab-separated-value spreadsheet, bulk release multiple datasets, bulk delete unpublished datasets, quickly duplicate records. replace licenses, and more. For additional information see their `PyPi page `_. + `dataverse-client-python `_ had its initial release in 2015. `Robert Liebowitz `_ created this library while at the `Center for Open Science (COS) `_ and the COS uses it to integrate the `Open Science Framework (OSF) `_ with Dataverse installations via an add-on which itself is open source and listed on the :doc:`/api/apps` page. `Pooch `_ is a Python library that allows library and application developers to download data. Among other features, it takes care of various protocols, caching in OS-specific locations, checksum verification and adds optional features like progress bars or log messages. Among other popular repositories, Pooch supports Dataverse in the sense that you can reference Dataverse-hosted datasets by just a DOI and Pooch will determine the data repository type, query the Dataverse API for contained files and checksums, giving you an easy interface to download them. From 467eb51166fe742f3506bef706f821a139e4c6b3 Mon Sep 17 00:00:00 2001 From: Oliver Bertuch Date: Mon, 18 Sep 2023 21:46:45 +0200 Subject: [PATCH 25/30] feat(ct): add labels with service dependency versions to images #9928 --- src/main/docker/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile index 88020a118b5..aa39078fb06 100644 --- a/src/main/docker/Dockerfile +++ b/src/main/docker/Dockerfile @@ -51,4 +51,6 @@ LABEL org.opencontainers.image.created="@git.build.time@" \ org.opencontainers.image.vendor="Global Dataverse Community Consortium" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.title="Dataverse Application Image" \ - org.opencontainers.image.description="This container image provides the research data repository software Dataverse in a box." \ No newline at end of file + org.opencontainers.image.description="This container image provides the research data repository software Dataverse in a box." \ + org.dataverse.deps.postgresql.version="@postgresql.server.version@" \ + org.dataverse.deps.solr.version="@solr.version@" \ No newline at end of file From 6b3c583c6c2b730bb5ab892010da37e2889d5bbe Mon Sep 17 00:00:00 2001 From: Philip Durbin Date: Mon, 18 Sep 2023 21:55:47 -0400 Subject: [PATCH 26/30] use a ref #9689 --- doc/sphinx-guides/source/api/dataaccess.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/dataaccess.rst b/doc/sphinx-guides/source/api/dataaccess.rst index 7b1feed7814..6a726cdc7b9 100755 --- a/doc/sphinx-guides/source/api/dataaccess.rst +++ b/doc/sphinx-guides/source/api/dataaccess.rst @@ -83,7 +83,7 @@ Basic access URI: ``/api/access/datafile/$id`` -.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. For further information, refer to `FilePIDsEnabled `_ +.. note:: Files can be accessed using persistent identifiers. This is done by passing the constant ``:persistentId`` where the numeric id of the file is expected, and then passing the actual persistent id as a query parameter with the name ``persistentId``. However, this file access method is only effective when the FilePIDsEnabled option is enabled, which can be authorized by the admin. For further information, refer to :ref:`:FilePIDsEnabled`. Example: Getting the file whose DOI is *10.5072/FK2/J8SJZB* :: From 30d1bffd126a2465117bccff1d4e97269c5500e0 Mon Sep 17 00:00:00 2001 From: Oliver Bertuch Date: Tue, 19 Sep 2023 16:22:11 +0200 Subject: [PATCH 27/30] feat(ct): make configbaker able to expose data from bootstrap process via env file #9933 --- .../scripts/bootstrap.sh | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/modules/container-configbaker/scripts/bootstrap.sh b/modules/container-configbaker/scripts/bootstrap.sh index 1aa9e232953..d09c1be5414 100644 --- a/modules/container-configbaker/scripts/bootstrap.sh +++ b/modules/container-configbaker/scripts/bootstrap.sh @@ -5,16 +5,17 @@ set -euo pipefail function usage() { - echo "Usage: $(basename "$0") [-h] [-u instanceUrl] [-t timeout] []" + echo "Usage: $(basename "$0") [-h] [-u instanceUrl] [-t timeout] [-e targetEnvFile] []" echo "" echo "Execute initial configuration (bootstrapping) of an empty Dataverse instance." echo -n "Known personas: " find "${BOOTSTRAP_DIR}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | paste -sd ' ' echo "" echo "Parameters:" - echo "instanceUrl - Location on container network where to reach your instance. Default: 'http://dataverse:8080'" - echo " timeout - Provide how long to wait for the instance to become available (using wait4x). Default: '2m'" - echo " persona - Configure persona to execute. Calls ${BOOTSTRAP_DIR}//init.sh. Default: 'base'" + echo " instanceUrl - Location on container network where to reach your instance. Default: 'http://dataverse:8080'" + echo " timeout - Provide how long to wait for the instance to become available (using wait4x). Default: '2m'" + echo "targetEnvFile - Path to a file where the bootstrap process can expose information as env vars (e.g. dataverseAdmin's API token)" + echo " persona - Configure persona to execute. Calls ${BOOTSTRAP_DIR}//init.sh. Default: 'base'" echo "" echo "Note: This script will wait for the Dataverse instance to be available before executing the bootstrapping." echo " It also checks if already bootstrapped before (availability of metadata blocks) and skip if true." @@ -25,12 +26,14 @@ function usage() { # Set some defaults as documented DATAVERSE_URL=${DATAVERSE_URL:-"http://dataverse:8080"} TIMEOUT=${TIMEOUT:-"2m"} +TARGET_ENV_FILE=${TARGET_ENV_FILE:-""} -while getopts "u:t:h" OPTION +while getopts "u:t:e:h" OPTION do case "$OPTION" in u) DATAVERSE_URL="$OPTARG" ;; t) TIMEOUT="$OPTARG" ;; + e) TARGET_ENV_FILE="$OPTARG" ;; h) usage;; \?) usage;; esac @@ -54,6 +57,21 @@ if [[ $BLOCK_COUNT -gt 0 ]]; then exit 0 fi +# Provide a space to store environment variables output to +ENV_OUT=$(mktemp) +export ENV_OUT + # Now execute the bootstrapping script echo "Now executing bootstrapping script at ${BOOTSTRAP_DIR}/${PERSONA}/init.sh." -exec "${BOOTSTRAP_DIR}/${PERSONA}/init.sh" +# shellcheck disable=SC1090 +source "${BOOTSTRAP_DIR}/${PERSONA}/init.sh" + +# If the env file option was given, check if the file is writeable and copy content from the temporary file +if [[ -n "${TARGET_ENV_FILE}" ]]; then + if [[ -f "${TARGET_ENV_FILE}" && -w "${TARGET_ENV_FILE}" ]]; then + cat "${ENV_OUT}" > "${TARGET_ENV_FILE}" + else + echo "File ${TARGET_ENV_FILE} not found, is a directory or not writeable" + exit 2 + fi +fi From 9d76d13546fca546ce06049576254da77a5b7cab Mon Sep 17 00:00:00 2001 From: Oliver Bertuch Date: Tue, 19 Sep 2023 16:23:13 +0200 Subject: [PATCH 28/30] feat(ct): make configbaker bootstrap dev persona output api token #9933 --- modules/container-configbaker/scripts/bootstrap/dev/init.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/container-configbaker/scripts/bootstrap/dev/init.sh b/modules/container-configbaker/scripts/bootstrap/dev/init.sh index 1042478963d..efdaee3d0c3 100644 --- a/modules/container-configbaker/scripts/bootstrap/dev/init.sh +++ b/modules/container-configbaker/scripts/bootstrap/dev/init.sh @@ -17,6 +17,8 @@ curl "${DATAVERSE_URL}/api/admin/settings/:DoiProvider" -X PUT -d FAKE API_TOKEN=$(grep apiToken "/tmp/setup-all.sh.out" | jq ".data.apiToken" | tr -d \") export API_TOKEN +# ${ENV_OUT} comes from bootstrap.sh and will expose the saved information back to the host if enabled. +echo "API_TOKEN=${API_TOKEN}" >> "${ENV_OUT}" echo "Publishing root dataverse..." curl -H "X-Dataverse-key:$API_TOKEN" -X POST "${DATAVERSE_URL}/api/dataverses/:root/actions/:publish" From 3d1521fdb4a1cfc9b89325e9f4085e73bc3f6435 Mon Sep 17 00:00:00 2001 From: Don Sizemore Date: Tue, 19 Sep 2023 14:22:15 -0400 Subject: [PATCH 29/30] #9931 document harvesting server IT config --- doc/sphinx-guides/source/developers/testing.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/sphinx-guides/source/developers/testing.rst b/doc/sphinx-guides/source/developers/testing.rst index acaeccf4f23..81e820fb869 100755 --- a/doc/sphinx-guides/source/developers/testing.rst +++ b/doc/sphinx-guides/source/developers/testing.rst @@ -225,6 +225,20 @@ If ``dataverse.siteUrl`` is absent, you can add it with: ``./asadmin create-jvm-options "-Ddataverse.siteUrl=http\://localhost\:8080"`` +dataverse.oai.server.maxidentifiers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The OAI Harvesting tests require that the paging limit for ListIdentifiers must be set to 2, in order to be able to trigger this paging behavior without having to create and export too many datasets: + +``./asadmin create-jvm-options "-Ddataverse.oai.server.maxidentifiers=2"`` + +dataverse.oai.server.maxrecords +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The OAI Harvesting tests require that the paging limit for ListRecords must be set to 2, in order to be able to trigger this paging behavior without having to create and export too many datasets: + +``./asadmin create-jvm-options "-Ddataverse.oai.server.maxrecords=2"`` + Identifier Generation ^^^^^^^^^^^^^^^^^^^^^ From 2e2fb380aad84d92253064ab3c58c0293b7d6c8b Mon Sep 17 00:00:00 2001 From: GPortas Date: Wed, 20 Sep 2023 05:53:29 +0100 Subject: [PATCH 30/30] Added: getFileDataTables endpoint permission checks for restricted and embargoed files --- .../edu/harvard/iq/dataverse/api/Files.java | 25 ++++++++++++++----- .../edu/harvard/iq/dataverse/api/FilesIT.java | 18 +++++++++++-- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Files.java b/src/main/java/edu/harvard/iq/dataverse/api/Files.java index 4c411a631f1..fec60f10f3f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Files.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Files.java @@ -18,6 +18,7 @@ import edu.harvard.iq.dataverse.TermsOfUseAndAccessValidator; import edu.harvard.iq.dataverse.UserNotificationServiceBean; import edu.harvard.iq.dataverse.api.auth.AuthRequired; +import edu.harvard.iq.dataverse.authorization.Permission; import edu.harvard.iq.dataverse.authorization.users.ApiToken; import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; import edu.harvard.iq.dataverse.authorization.users.User; @@ -79,6 +80,8 @@ import static edu.harvard.iq.dataverse.util.json.JsonPrinter.jsonDT; import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.FORBIDDEN; + import jakarta.ws.rs.core.UriInfo; import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; @@ -840,12 +843,22 @@ public Response getFileDownloadCount(@Context ContainerRequestContext crc, @Path @AuthRequired @Path("{id}/dataTables") public Response getFileDataTables(@Context ContainerRequestContext crc, @PathParam("id") String dataFileId) { - return response(req -> { - DataFile dataFile = execCommand(new GetDataFileCommand(req, findDataFileOrDie(dataFileId))); - if (!dataFile.isTabularData()) { - return error(BAD_REQUEST, "This operation is only available for tabular files."); + DataFile dataFile; + try { + dataFile = findDataFileOrDie(dataFileId); + } catch (WrappedResponse e) { + return error(Response.Status.NOT_FOUND, "File not found for given id."); + } + if (dataFile.isRestricted() || FileUtil.isActivelyEmbargoed(dataFile)) { + DataverseRequest dataverseRequest = createDataverseRequest(getRequestUser(crc)); + boolean hasPermissionToDownloadFile = permissionSvc.requestOn(dataverseRequest, dataFile).has(Permission.DownloadFile); + if (!hasPermissionToDownloadFile) { + return error(FORBIDDEN, "Insufficient permissions to access the requested information."); } - return ok(jsonDT(dataFile.getDataTables())); - }, getRequestUser(crc)); + } + if (!dataFile.isTabularData()) { + return error(BAD_REQUEST, "This operation is only available for tabular files."); + } + return ok(jsonDT(dataFile.getDataTables())); } } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java index de91e5644cf..0a16bca7008 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java @@ -2190,11 +2190,25 @@ public void testGetFileDataTables() throws InterruptedException { // Ensure tabular file is ingested sleep(2000); + String testTabularFileId = Integer.toString(JsonPath.from(uploadTabularFileResponse.body().asString()).getInt("data.files[0].dataFile.id")); + // Get file data tables for the tabular file and assert data is obtained - int testTabularFileId = JsonPath.from(uploadTabularFileResponse.body().asString()).getInt("data.files[0].dataFile.id"); - Response getFileDataTablesForTabularFileResponse = UtilIT.getFileDataTables(Integer.toString(testTabularFileId), apiToken); + Response getFileDataTablesForTabularFileResponse = UtilIT.getFileDataTables(testTabularFileId, apiToken); getFileDataTablesForTabularFileResponse.then().assertThat().statusCode(OK.getStatusCode()); int dataTablesNumber = JsonPath.from(getFileDataTablesForTabularFileResponse.body().asString()).getList("data").size(); assertTrue(dataTablesNumber > 0); + + // Get file data tables for a restricted tabular file as the owner and assert data is obtained + Response restrictFileResponse = UtilIT.restrictFile(testTabularFileId, true, apiToken); + restrictFileResponse.then().assertThat().statusCode(OK.getStatusCode()); + getFileDataTablesForTabularFileResponse = UtilIT.getFileDataTables(testTabularFileId, apiToken); + getFileDataTablesForTabularFileResponse.then().assertThat().statusCode(OK.getStatusCode()); + + // Get file data tables for a restricted tabular file as other user and assert forbidden error is thrown + Response createRandomUser = UtilIT.createRandomUser(); + createRandomUser.then().assertThat().statusCode(OK.getStatusCode()); + String randomUserApiToken = UtilIT.getApiTokenFromResponse(createRandomUser); + getFileDataTablesForTabularFileResponse = UtilIT.getFileDataTables(testTabularFileId, randomUserApiToken); + getFileDataTablesForTabularFileResponse.then().assertThat().statusCode(FORBIDDEN.getStatusCode()); } }