diff --git a/cadc-inventory-server/src/main/java/org/opencadc/inventory/transfer/GetKeyAction.java b/cadc-inventory-server/src/main/java/org/opencadc/inventory/transfer/GetKeyAction.java index e3396d87..d42c1335 100644 --- a/cadc-inventory-server/src/main/java/org/opencadc/inventory/transfer/GetKeyAction.java +++ b/cadc-inventory-server/src/main/java/org/opencadc/inventory/transfer/GetKeyAction.java @@ -70,6 +70,7 @@ import ca.nrc.cadc.rest.InlineContentHandler; import ca.nrc.cadc.rest.RestAction; import java.io.OutputStream; +import java.io.PrintWriter; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; @@ -111,7 +112,14 @@ public void doAction() throws Exception { ostream.flush(); } } catch (NamingException ex) { - throw new RuntimeException("BUG: failed to find keys via JNDI", ex); + syncOutput.setHeader("content-type", "test/plain"); + syncOutput.setCode(404); + try (OutputStream ostream = syncOutput.getOutputStream()) { + PrintWriter w = new PrintWriter(ostream); + w.println("not found: key signing disabled"); + w.flush(); + w.close(); + } } } } diff --git a/cadc-inventory-server/src/main/java/org/opencadc/inventory/transfer/ProtocolsGenerator.java b/cadc-inventory-server/src/main/java/org/opencadc/inventory/transfer/ProtocolsGenerator.java index 5cf4a532..0307e7c1 100644 --- a/cadc-inventory-server/src/main/java/org/opencadc/inventory/transfer/ProtocolsGenerator.java +++ b/cadc-inventory-server/src/main/java/org/opencadc/inventory/transfer/ProtocolsGenerator.java @@ -499,29 +499,31 @@ private List doPushTo(URI artifactURI, Transfer transfer, String authT if (sec == null) { sec = Standards.SECURITY_METHOD_ANON; } - boolean incToken = Standards.SECURITY_METHOD_ANON.equals(sec); + boolean anon = Standards.SECURITY_METHOD_ANON.equals(sec); Interface iface = filesCap.findInterface(sec); log.debug("PUT: " + storageSite + " proto: " + proto + " iface: " + iface); if (iface != null) { URL baseURL = iface.getAccessURL().getURL(); //log.debug("base url for site " + storageSite.getResourceID() + ": " + baseURL); if (protocolCompat(proto, baseURL)) { - - StringBuilder sb = new StringBuilder(); - sb.append(baseURL.toExternalForm()).append("/"); - if (authToken != null && incToken) { - sb.append(authToken).append("/"); - } - sb.append(artifactURI.toASCIIString()); - Protocol p = new Protocol(proto.getUri()); - if (transfer.version == VOS.VOSPACE_21) { - p.setSecurityMethod(proto.getSecurityMethod()); + // // no plain anon URL for put: !anon or anon+token + boolean gen = (!anon || (anon && authToken != null)); + if (gen) { + StringBuilder sb = new StringBuilder(); + sb.append(baseURL.toExternalForm()).append("/"); + if (authToken != null && anon) { + sb.append(authToken).append("/"); + } + sb.append(artifactURI.toASCIIString()); + Protocol p = new Protocol(proto.getUri()); + if (transfer.version == VOS.VOSPACE_21) { + p.setSecurityMethod(proto.getSecurityMethod()); + } + p.setEndpoint(sb.toString()); + protos.add(p); + log.debug("added: " + p); } - p.setEndpoint(sb.toString()); - protos.add(p); - log.debug("added: " + p); - - // no plain anon URL for put + } else { log.debug("PUT: " + storageSite + "PUT: reject protocol: " + proto + " reason: no compatible URL protocol"); diff --git a/raven/README.md b/raven/README.md index c121b419..bd6948c3 100644 --- a/raven/README.md +++ b/raven/README.md @@ -42,29 +42,32 @@ org.opencadc.raven.inventory.schema={schema} # consistency settings org.opencadc.raven.consistency.preventNotFound=true|false - -# url signing key usage -org.opencadc.raven.keys.preauth={true|false} ``` The _preventNotFound_ key can be used to configure `raven` to prevent artifact-not-found errors that might result due to the eventual consistency nature of the system by directly checking for the artifact at _all known_ sites. This feature introduces an overhead for the genuine not-found cases. -The _keys.preauth_ key configures `raven` to use URL-signing. When enabled, `raven` can generate a signed token -and embeds it into the URL; `minoc` services can validate the token and grant access without further permission -checks. With transfer negotiation, the signed URL gets added as an additional "anonymous" URL. + The following optional keys configure raven to use external service(s) to obtain grant information in order -to perform authorization checks: +to perform authorization checks and generate signed URLs: ``` org.opencadc.raven.readGrantProvider={resourceID of a permission granting service} org.opencadc.raven.writeGrantProvider={resourceID of a permission granting service} + +# url signing key usage +org.opencadc.raven.keys.preauth={true|false} ``` The optional _readGrantProvider_ and _writeGrantProvider_ keys configure minoc to call other services to get grants (permissions) for operations. Multiple values of the permission granting service resourceID(s) may be provided by including multiple property settings. All services will be consulted but a single positive result is sufficient to grant permission for an action. +The _keys.preauth_ key (default: false) configures `raven` to use URL-signing. When enabled, `raven` can generate a signed token +and embed it into the URL; `minoc` services that are configured to trust a `raven` service will download the public key and can +validate the token and grant access without further permission checks. With transfer negotiation, the signed URL gets added as +an additional "anonymous" URL. + The following optional keys configure raven to prioritize sites returned in transfer negotiation, with higher priority sites first in the list of transfer URL's. Multiple values of _namespace_ may be specified for a single _resourceID_. The _namespace_ value(s) must end with a colon (:) or slash (/) so one namespace cannot accidentally match (be a diff --git a/raven/src/main/java/org/opencadc/raven/ArtifactAction.java b/raven/src/main/java/org/opencadc/raven/ArtifactAction.java index a4c4cf98..b49032c6 100644 --- a/raven/src/main/java/org/opencadc/raven/ArtifactAction.java +++ b/raven/src/main/java/org/opencadc/raven/ArtifactAction.java @@ -197,7 +197,7 @@ protected ArtifactAction() { this.preauthKeys = Boolean.valueOf(pak); log.debug("Using preauth keys: " + this.preauthKeys); } else { - throw new IllegalStateException("invalid config: missing keys.preauth configuration"); + this.preauthKeys = false; } } diff --git a/raven/src/main/java/org/opencadc/raven/HeadFilesAction.java b/raven/src/main/java/org/opencadc/raven/HeadFilesAction.java index a9dcfc65..4b3312eb 100644 --- a/raven/src/main/java/org/opencadc/raven/HeadFilesAction.java +++ b/raven/src/main/java/org/opencadc/raven/HeadFilesAction.java @@ -72,9 +72,11 @@ import ca.nrc.cadc.rest.SyncOutput; import java.net.HttpURLConnection; import java.net.URL; +import java.util.Set; import org.apache.log4j.Logger; import org.opencadc.inventory.Artifact; import org.opencadc.inventory.InventoryUtil; +import org.opencadc.inventory.StorageSite; import org.opencadc.inventory.db.StorageSiteDAO; import org.opencadc.inventory.transfer.ProtocolsGenerator; import org.opencadc.permissions.ReadGrant; @@ -108,8 +110,10 @@ public void doAction() throws Exception { log.debug("Starting HEAD action for " + artifactURI.toASCIIString()); Artifact artifact = artifactDAO.get(artifactURI); - if (artifact == null) { - if (this.preventNotFound) { + if (artifact == null && preventNotFound) { + StorageSiteDAO storageSiteDAO = new StorageSiteDAO(artifactDAO); + Set sites = storageSiteDAO.list(); + if (!sites.isEmpty()) { // check known storage sites ProtocolsGenerator pg = new ProtocolsGenerator( this.artifactDAO, this.siteAvailabilities, this.siteRules); @@ -117,14 +121,16 @@ public void doAction() throws Exception { pg.user = this.user; pg.preventNotFound = this.preventNotFound; pg.storageResolver = this.storageResolver; - StorageSiteDAO storageSiteDAO = new StorageSiteDAO(artifactDAO); + Transfer transfer = new Transfer(artifactURI, Direction.pullFromVoSpace); Protocol proto = new Protocol(VOS.PROTOCOL_HTTPS_GET); proto.setSecurityMethod(Standards.SECURITY_METHOD_ANON); transfer.getProtocols().add(proto); - // TODO: tokenGen is optional so this can fail - String authToken = tokenGen.generateToken(artifactURI, ReadGrant.class, user); - artifact = pg.getUnsyncedArtifact(artifactURI, transfer, storageSiteDAO.list(), authToken); + String authToken = null; + if (tokenGen != null) { + authToken = tokenGen.generateToken(artifactURI, ReadGrant.class, user); + } + artifact = pg.getUnsyncedArtifact(artifactURI, transfer, sites, authToken); } } diff --git a/raven/src/main/java/org/opencadc/raven/RavenInitAction.java b/raven/src/main/java/org/opencadc/raven/RavenInitAction.java index 95c6f0c0..aaee4867 100644 --- a/raven/src/main/java/org/opencadc/raven/RavenInitAction.java +++ b/raven/src/main/java/org/opencadc/raven/RavenInitAction.java @@ -330,15 +330,6 @@ static MultiValuedProperties getConfig() { sb.append("OK"); } - String preauthKeys = mvp.getFirstPropertyValue(RavenInitAction.PREAUTH_KEY); - sb.append("\n\t").append(RavenInitAction.PREAUTH_KEY).append(": "); - if (preauthKeys == null) { - sb.append("MISSING"); - ok = false; - } else { - sb.append("OK"); - } - if (!ok) { throw new IllegalStateException(sb.toString()); } diff --git a/vault/README.md b/vault/README.md index b2888f64..081fbefa 100644 --- a/vault/README.md +++ b/vault/README.md @@ -40,6 +40,12 @@ org.opencadc.vault.nodes.username={username for vospace pool} org.opencadc.vault.nodes.password={password for vospace pool} org.opencadc.vault.nodes.url=jdbc:postgresql://{server}/{database} +org.opencadc.vault.inventory.maxActive={max connections for inventory pool} +# optional: config for separate inventory pool +org.opencadc.vault.inventory.username={username for inventory pool} +org.opencadc.vault.inventory.password={password for inventory pool} +org.opencadc.vault.inventory.url=jdbc:postgresql://{server}/{database} + org.opencadc.vault.uws.maxActive={max connections for uws pool} org.opencadc.vault.uws.username={username for uws pool} org.opencadc.vault.uws.password={password for uws pool} @@ -50,6 +56,15 @@ all the content (insert, update, delete). The database is specified in the JDBC in the vault.properties (below). Failure to connect or initialize the database will show up in logs and in the VOSI-availability output. +The _inventory_ account owns and manages (create, alter, drop) inventory database objects and manages +all the content (update and delete Artifact, insert DeletedArtifactEvent). The database is specified +in the JDBC URL and the schema name is specified in the minoc.properties (below). Failure to connect or +initialize the database will show up in logs and in the VOSI-availability output. The _inventory_ content +may be in the same database as the _nodes_, in a different database in the same server, or in a different +server entirely. See `org.opencadc.vault.singlePool` below for the pros and cons. Note: it is a good +idea to set `maxActive` to a valid integer (e.g. 0) when using a single pool; this avoids an ugly but +meaningless stack trace in the logs at startup. + The _uws_ account owns and manages (create, alter, drop) uws database objects in the `uws` schema and manages all the content (insert, update, delete). The database is specified in the JDBC URLFailure to connect or initialize the database will show up in logs and in the VOSI-availability output. @@ -70,6 +85,7 @@ org.opencadc.vault.consistency.preventNotFound=true|false # vault database settings org.opencadc.vault.inventory.schema = {inventory schema name} org.opencadc.vault.vospace.schema = {vospace schema name} +org.opencadc.vault.singlePool = {true|false} # root container nodes org.opencadc.vault.root.owner = {owner of root node} @@ -92,6 +108,14 @@ to configuration limitations in luskan. The _vospace.schema_ name is the name of the database schema used for all created database objects (tables, indices, etc). Note that with a single connection pool, the two schemas must currently be in the same database. TODO: augment config to support separate inventory and vospace pools. +The _singlePool_ key configures `vault` to use a single pool (the _nodes_ pool) for both vospace and inventory +operations. The inventory and vospace content must be in the same database for this to work. When configured +to use a single pool, delete node operations can delete a DataNode and the associated Artifact and create the +DeletedArtifactEvent in a single transaction. When configured to use separate pools, the delete Artifact and create +DeletedArtifactEvent are done in a separate transaction and if that fails the Artifact will be left behind and +orphaned until the vault validation (see ???) runs and fixes such a discrepancy. However, _singlePool_ = `false` allows +the content to be stored in two separate databases or servers. + The _root.owner_ owns the root node and has full read and write permission in the root container, so it can create and delete container nodes at the root and assign container node properties that are normally read-only to normal users: owner, quota, etc. This must be set to the username of the admin. diff --git a/vault/VERSION b/vault/VERSION index 71963f95..9d05f397 100644 --- a/vault/VERSION +++ b/vault/VERSION @@ -4,6 +4,6 @@ # tags with and without build number so operators use the versioned # tag but we always keep a timestamped tag in case a semantic tag gets # replaced accidentally -VER=0.1.0 +VER=0.2.0 TAGS="${VER} ${VER}-$(date --utc +"%Y%m%dT%H%M%S")" unset VER diff --git a/vault/src/main/java/org/opencadc/vault/NodePersistenceImpl.java b/vault/src/main/java/org/opencadc/vault/NodePersistenceImpl.java index 5f3ce17f..0e352fcc 100644 --- a/vault/src/main/java/org/opencadc/vault/NodePersistenceImpl.java +++ b/vault/src/main/java/org/opencadc/vault/NodePersistenceImpl.java @@ -155,7 +155,11 @@ public class NodePersistenceImpl implements NodePersistence { ) ); - private final Map nodeDaoConfig = new TreeMap<>(); + private final Map nodeDaoConfig; + private final Map invDaoConfig; + private final Map kpDaoConfig; + private final boolean singlePool; + private final ContainerNode root; private final Namespace storageNamespace; @@ -173,13 +177,10 @@ public NodePersistenceImpl(URI resourceID) { } this.resourceID = resourceID; MultiValuedProperties config = VaultInitAction.getConfig(); - String dataSourceName = VaultInitAction.JNDI_DATASOURCE; - String inventorySchema = config.getFirstPropertyValue(VaultInitAction.INVENTORY_SCHEMA_KEY); - String vospaceSchema = config.getFirstPropertyValue(VaultInitAction.VOSPACE_SCHEMA_KEY); - nodeDaoConfig.put(SQLGenerator.class.getName(), SQLGenerator.class); - nodeDaoConfig.put("jndiDataSourceName", dataSourceName); - nodeDaoConfig.put("schema", inventorySchema); - nodeDaoConfig.put("vosSchema", vospaceSchema); + this.nodeDaoConfig = VaultInitAction.getDaoConfig(config); + this.invDaoConfig = VaultInitAction.getInvConfig(config); + this.kpDaoConfig = VaultInitAction.getKeyPairConfig(config); + this.singlePool = nodeDaoConfig.get("jndiDataSourceName").equals(invDaoConfig.get("jndiDataSourceName")); // root node IdentityManager identityManager = AuthenticationUtil.getIdentityManager(); @@ -224,7 +225,7 @@ public Views getViews() { @Override public TransferGenerator getTransferGenerator() { PreauthKeyPairDAO keyDAO = new PreauthKeyPairDAO(); - keyDAO.setConfig(nodeDaoConfig); + keyDAO.setConfig(kpDaoConfig); PreauthKeyPair kp = keyDAO.get(VaultInitAction.KEY_PAIR_NAME); TokenTool tt = new TokenTool(kp.getPublicKey(), kp.getPrivateKey()); return new VaultTransferGenerator(this, getArtifactDAO(), tt, preventNotFound); @@ -480,9 +481,28 @@ public Node put(Node node) throws NodeNotSupportedException, TransientException } } + NodeProperty contentType = null; + NodeProperty contentEncoding = null; + // need to remove all artifact props from the node.getProperties() + // and use artifactDAO to set the mutable ones + Iterator i = node.getProperties().iterator(); + while (i.hasNext()) { + NodeProperty np = i.next(); + if (VOS.PROPERTY_URI_TYPE.equals(np.getKey())) { + contentType = np; + } else if (VOS.PROPERTY_URI_CONTENTENCODING.equals(np.getKey())) { + contentEncoding = np; + } + + if (ARTIFACT_PROPS.contains(np.getKey())) { + i.remove(); + } + } + + ArtifactDAO artifactDAO = null; + Artifact a = null; if (node instanceof DataNode) { DataNode dn = (DataNode) node; - boolean knownNoArtifact = false; if (dn.storageID == null) { // new data node? if lastModified is assigned, this looks sketchy if (dn.getLastModified() != null) { @@ -494,50 +514,45 @@ public Node put(Node node) throws NodeNotSupportedException, TransientException // once someone puts the file to minoc, so Node.storageID == Artifact.uri // but the artifact may or may not exist dn.storageID = generateStorageID(); - knownNoArtifact = true; - } - - // need to remove all artifact props from the node.getProperties() - // and use artifactDAO to set the mutable ones - NodeProperty contentType = null; - NodeProperty contentEncoding = null; - Iterator i = dn.getProperties().iterator(); - while (i.hasNext()) { - NodeProperty np = i.next(); - if (VOS.PROPERTY_URI_TYPE.equals(np.getKey())) { - contentType = np; - } else if (VOS.PROPERTY_URI_CONTENTENCODING.equals(np.getKey())) { - contentEncoding = np; - } - - if (ARTIFACT_PROPS.contains(np.getKey())) { - i.remove(); - } - } - // optimization: avoid query when we know artifact doesn't exist - // and: avoid query if we don't need to update it - if (!knownNoArtifact && (contentType != null || contentEncoding != null)) { - ArtifactDAO artifactDAO = getArtifactDAO(); - Artifact a = artifactDAO.get(dn.storageID); - log.warn("put: " + contentType + " " + contentEncoding + " -> " + a); - if (a != null) { - if (contentType == null) { - a.contentType = null; - } else { - a.contentType = contentType.getValue(); - } - if (contentEncoding == null) { - a.contentEncoding = null; - } else { - a.contentEncoding = contentEncoding.getValue(); - } - artifactDAO.put(a); + } else { + if (contentType != null || contentEncoding != null) { + // update possibly required + artifactDAO = getArtifactDAO(); + a = artifactDAO.get(dn.storageID); + } else { + log.debug("no artifact props to update - skipping ArtifactDAO.get"); } } } + + boolean useTxn = singlePool && a != null; // TODO + // update node NodeDAO dao = getDAO(); dao.put(node); + + // update artifact after node + if (a != null) { + if (contentType == null || contentType.isMarkedForDeletion()) { + a.contentType = null; + } else { + a.contentType = contentType.getValue(); + } + if (contentEncoding == null || contentEncoding.isMarkedForDeletion()) { + a.contentEncoding = null; + } else { + a.contentEncoding = contentEncoding.getValue(); + } + artifactDAO.put(a); + + // re-add node props + if (contentType != null && !contentType.isMarkedForDeletion()) { + node.getProperties().add(contentType); + } + if (contentEncoding != null && !contentEncoding.isMarkedForDeletion()) { + node.getProperties().add(contentEncoding); + } + } return node; } @@ -583,12 +598,21 @@ public void delete(Node node) throws TransientException { throw new IllegalArgumentException("arg cannot be null: node"); } + Artifact a = null; final NodeDAO dao = getDAO(); final ArtifactDAO artifactDAO = getArtifactDAO(); TransactionManager txn = dao.getTransactionManager(); - + TransactionManager atxn = null; try { - log.debug("starting transaction"); + if (node instanceof DataNode) { + DataNode dn = (DataNode) node; + a = artifactDAO.get(dn.storageID); + } + if (a != null && !singlePool) { + atxn = artifactDAO.getTransactionManager(); + } + + log.debug("starting node transaction"); txn.startTransaction(); log.debug("start txn: OK"); @@ -614,30 +638,52 @@ public void delete(Node node) throws TransientException { } } // else: LinkNode can always be deleted - if (storageID != null) { - Artifact a = artifactDAO.get(storageID); - if (a != null) { - DeletedArtifactEventDAO daeDAO = new DeletedArtifactEventDAO(artifactDAO); - DeletedArtifactEvent dae = new DeletedArtifactEvent(a.getID()); - daeDAO.put(dae); - artifactDAO.delete(a.getID()); - } + if (singlePool && a != null) { + // inventory ops inside main txn + DeletedArtifactEventDAO daeDAO = new DeletedArtifactEventDAO(artifactDAO); + DeletedArtifactEvent dae = new DeletedArtifactEvent(a.getID()); + daeDAO.put(dae); + artifactDAO.delete(a.getID()); } + // TODO: need DeletedNodeDAO to create DeletedNodeEvent dao.delete(node.getID()); } else { log.debug("failed to lock node " + node.getID() + " - assume deleted by another process"); } - + log.debug("commit txn..."); txn.commitTransaction(); log.debug("commit txn: OK"); + + if (!singlePool && a != null) { + log.debug("starting artifact transaction"); + atxn.startTransaction(); + log.debug("start txn: OK"); + + Artifact alock = artifactDAO.lock(a); + if (alock != null) { + DeletedArtifactEventDAO daeDAO = new DeletedArtifactEventDAO(artifactDAO); + DeletedArtifactEvent dae = new DeletedArtifactEvent(alock.getID()); + daeDAO.put(dae); + artifactDAO.delete(alock.getID()); + } + log.debug("commit artifact txn..."); + atxn.commitTransaction(); + atxn = null; + log.debug("commit artifact txn: OK"); + } } catch (Exception ex) { if (txn.isOpen()) { log.error("failed to delete " + node.getID() + " aka " + node.getName(), ex); txn.rollbackTransaction(); log.debug("rollback txn: OK"); } + if (atxn != null && atxn.isOpen()) { + log.error("failed to delete " + a.getID() + " aka " + a.getURI(), ex); + atxn.rollbackTransaction(); + log.debug("rollback artifact txn: OK"); + } throw ex; } finally { if (txn.isOpen()) { @@ -645,6 +691,11 @@ public void delete(Node node) throws TransientException { txn.rollbackTransaction(); log.error("rollback txn: OK"); } + if (atxn != null && atxn.isOpen()) { + log.error("BUG - open artifact transaction in finally"); + atxn.rollbackTransaction(); + log.error("rollback artifact txn: OK"); + } } } } diff --git a/vault/src/main/java/org/opencadc/vault/VaultInitAction.java b/vault/src/main/java/org/opencadc/vault/VaultInitAction.java index a9e37e9e..2f8625d3 100644 --- a/vault/src/main/java/org/opencadc/vault/VaultInitAction.java +++ b/vault/src/main/java/org/opencadc/vault/VaultInitAction.java @@ -88,6 +88,7 @@ import org.opencadc.inventory.db.PreauthKeyPairDAO; import org.opencadc.inventory.db.SQLGenerator; import org.opencadc.inventory.db.StorageSiteDAO; +import org.opencadc.inventory.db.version.InitDatabase; import org.opencadc.inventory.transfer.StorageSiteAvailabilityCheck; import org.opencadc.vospace.db.InitDatabaseVOS; import org.opencadc.vospace.server.NodePersistence; @@ -103,7 +104,8 @@ public class VaultInitAction extends InitAction { static String KEY_PAIR_NAME = "vault-preauth-keys"; - static final String JNDI_DATASOURCE = "jdbc/nodes"; // context.xml + static final String JNDI_VOS_DATASOURCE = "jdbc/nodes"; // context.xml + static final String JNDI_INV_DATASOURCE = "jdbc/inventory"; // context.xml static final String JNDI_UWS_DATASOURCE = "jdbc/uws"; // context.xml // config keys @@ -112,6 +114,7 @@ public class VaultInitAction extends InitAction { static final String PREVENT_NOT_FOUND_KEY = VAULT_KEY + ".consistency.preventNotFound"; static final String INVENTORY_SCHEMA_KEY = VAULT_KEY + ".inventory.schema"; static final String VOSPACE_SCHEMA_KEY = VAULT_KEY + ".vospace.schema"; + static final String SINGLE_POOL_KEY = VAULT_KEY + ".singlePool"; static final String ROOT_OWNER = VAULT_KEY + ".root.owner"; // numeric? @@ -120,7 +123,8 @@ public class VaultInitAction extends InitAction { MultiValuedProperties props; private URI resourceID; private Namespace storageNamespace; - private Map daoConfig; + private Map vosDaoConfig; + private Map invDaoConfig; private String jndiNodePersistence; private String jndiPreauthKeys; @@ -209,6 +213,15 @@ static MultiValuedProperties getConfig() { } else { sb.append("OK"); } + + String sp = mvp.getFirstPropertyValue(SINGLE_POOL_KEY); + sb.append("\n\t").append(SINGLE_POOL_KEY).append(": "); + if (sp == null) { + sb.append("MISSING"); + ok = false; + } else { + sb.append("OK"); + } String ns = mvp.getFirstPropertyValue(STORAGE_NAMESPACE_KEY); sb.append("\n\t").append(STORAGE_NAMESPACE_KEY).append(": "); @@ -227,12 +240,32 @@ static MultiValuedProperties getConfig() { } static Map getDaoConfig(MultiValuedProperties props) { - Map ret = new TreeMap<>(); ret.put(SQLGenerator.class.getName(), SQLGenerator.class); // not configurable right now - ret.put("jndiDataSourceName", org.opencadc.vault.VaultInitAction.JNDI_DATASOURCE); - ret.put("schema", props.getFirstPropertyValue(org.opencadc.vault.VaultInitAction.INVENTORY_SCHEMA_KEY)); - ret.put("vosSchema", props.getFirstPropertyValue(org.opencadc.vault.VaultInitAction.VOSPACE_SCHEMA_KEY)); + ret.put("jndiDataSourceName", VaultInitAction.JNDI_VOS_DATASOURCE); + // unused, but inventory "schema" required by cadc-inventory-db + ret.put("schema", props.getFirstPropertyValue(INVENTORY_SCHEMA_KEY)); + ret.put("vosSchema", props.getFirstPropertyValue(VOSPACE_SCHEMA_KEY)); + return ret; + } + + static Map getInvConfig(MultiValuedProperties props) { + boolean usp = Boolean.parseBoolean(props.getFirstPropertyValue(SINGLE_POOL_KEY)); + if (usp) { + return getDaoConfig(props); + } + Map ret = new TreeMap<>(); + ret.put(SQLGenerator.class.getName(), SQLGenerator.class); // not configurable right now + ret.put("jndiDataSourceName", JNDI_INV_DATASOURCE); + ret.put("schema", props.getFirstPropertyValue(INVENTORY_SCHEMA_KEY)); + return ret; + } + + static Map getKeyPairConfig(MultiValuedProperties props) { + Map ret = new TreeMap<>(); + ret.put(SQLGenerator.class.getName(), SQLGenerator.class); // not configurable right now + ret.put("jndiDataSourceName", JNDI_VOS_DATASOURCE); + ret.put("schema", props.getFirstPropertyValue(VOSPACE_SCHEMA_KEY)); return ret; } @@ -244,7 +277,8 @@ private void initConfig() { try { this.resourceID = new URI(rid); this.storageNamespace = new Namespace(ns); - this.daoConfig = getDaoConfig(props); + this.vosDaoConfig = getDaoConfig(props); + this.invDaoConfig = getInvConfig(props); log.info("initConfig: OK"); } catch (URISyntaxException ex) { throw new IllegalStateException("invalid config: " + RESOURCE_ID_KEY + " must be a valid URI"); @@ -252,33 +286,45 @@ private void initConfig() { } private void initDatabase() { - log.info("initDatabase: START"); try { - DataSource ds = DBUtil.findJNDIDataSource(JNDI_DATASOURCE); - String schema = (String) daoConfig.get("vosSchema"); + String dsname = (String) vosDaoConfig.get("jndiDataSourceName"); + String schema = (String) vosDaoConfig.get("vosSchema"); + log.info("initDatabase: " + dsname + " " + schema + " START"); + DataSource ds = DBUtil.findJNDIDataSource(dsname); InitDatabaseVOS init = new InitDatabaseVOS(ds, null, schema); init.doInit(); - log.info("initDatabase: " + JNDI_DATASOURCE + " " + schema + " OK"); + log.info("initDatabase: " + dsname + " " + schema + " OK"); } catch (Exception ex) { - throw new IllegalStateException("check/init database failed", ex); + throw new IllegalStateException("check/init vospace database failed", ex); + } + + try { + String dsname = (String) invDaoConfig.get("jndiDataSourceName"); + String schema = (String) invDaoConfig.get("schema"); + log.info("initDatabase: " + dsname + " " + schema + " START"); + DataSource ds = DBUtil.findJNDIDataSource(dsname); + InitDatabase init = new InitDatabase(ds, null, schema); + init.doInit(); + log.info("initDatabase: " + dsname + " " + schema + " OK"); + } catch (Exception ex) { + throw new IllegalStateException("check/init inventory database failed", ex); } } private void initUWSDatabase() { - log.info("initUWSDatabase: START"); try { - // Init UWS database + log.info("initDatabase: " + JNDI_UWS_DATASOURCE + " uws START"); DataSource uws = DBUtil.findJNDIDataSource(JNDI_UWS_DATASOURCE); InitDatabaseUWS uwsi = new InitDatabaseUWS(uws, null, "uws"); uwsi.doInit(); log.info("initDatabase: " + JNDI_UWS_DATASOURCE + " uws OK"); - } catch (Exception ex) { throw new RuntimeException("check/init uws database failed", ex); } } private void initNodePersistence() { + log.info("initNodePersistence: START"); jndiNodePersistence = appName + "-" + NodePersistence.class.getName(); try { Context ctx = new InitialContext(); @@ -290,7 +336,7 @@ private void initNodePersistence() { NodePersistence npi = new NodePersistenceImpl(resourceID); ctx.bind(jndiNodePersistence, npi); - log.info("created JNDI key: " + jndiNodePersistence + " impl: " + npi.getClass().getName()); + log.info("initNodePersistence: created JNDI key: " + jndiNodePersistence + " impl: " + npi.getClass().getName()); } catch (Exception ex) { log.error("Failed to create JNDI Key " + jndiNodePersistence, ex); } @@ -298,10 +344,10 @@ private void initNodePersistence() { private void initKeyPair() { log.info("initKeyPair: START"); - jndiPreauthKeys = appName + "-" + PreauthKeyPair.class.getName(); + //jndiPreauthKeys = appName + "-" + PreauthKeyPair.class.getName(); try { PreauthKeyPairDAO dao = new PreauthKeyPairDAO(); - dao.setConfig(daoConfig); + dao.setConfig(getKeyPairConfig(props)); PreauthKeyPair keys = dao.get(KEY_PAIR_NAME); if (keys == null) { KeyPair kp = RsaSignatureGenerator.getKeyPair(4096); @@ -322,6 +368,7 @@ private void initKeyPair() { } else { log.info("initKeyPair: re-use existing keys - OK"); } + /* Context ctx = new InitialContext(); try { ctx.unbind(jndiPreauthKeys); @@ -333,6 +380,7 @@ private void initKeyPair() { Object o = ctx.lookup(jndiPreauthKeys); log.info("checking... found: " + jndiPreauthKeys + " = " + o + " in " + ctx); + */ } catch (Exception ex) { throw new RuntimeException("check/init " + KEY_PAIR_NAME + " failed", ex); } diff --git a/vault/src/main/webapp/META-INF/context.xml b/vault/src/main/webapp/META-INF/context.xml index e5775723..ebd23f61 100644 --- a/vault/src/main/webapp/META-INF/context.xml +++ b/vault/src/main/webapp/META-INF/context.xml @@ -15,6 +15,18 @@ removeAbandoned="false" testOnBorrow="true" validationQuery="select 123" /> + +