[java-identity-provider] branch main updated: IDP-1664 - Support Module service API
Scott Cantor
cantor.2 at osu.edu
Tue Sep 8 19:19:41 UTC 2020
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=ad65818cbc67e2c0526cd3d926310b96b45de784
The following commit(s) were added to refs/heads/main by this push:
new ad65818cb IDP-1664 - Support Module service API
ad65818cb is described below
commit ad65818cbc67e2c0526cd3d926310b96b45de784
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Sep 8 15:19:32 2020 -0400
IDP-1664 - Support Module service API
https://issues.shibboleth.net/jira/browse/IDP-1664
Add resource reporting to enable/disable API.
Add reporting and module messaging to CLI.
Add ANSI color for non-Windows output.
---
.../shibboleth/idp/module/AbstractIdPModule.java | 86 +++++++++++++----
.../java/net/shibboleth/idp/module/IdPModule.java | 32 ++++++-
.../idp/module/PropertyDrivenIdPModule.java | 16 +++-
.../.settings/org.eclipse.jdt.core.prefs | 2 +-
.../idp/module/impl/ModuleManagerArguments.java | 3 +-
.../idp/module/impl/ModuleManagerCLI.java | 103 +++++++++++++++++++--
6 files changed, 208 insertions(+), 34 deletions(-)
diff --git a/idp-admin-api/src/main/java/net/shibboleth/idp/module/AbstractIdPModule.java b/idp-admin-api/src/main/java/net/shibboleth/idp/module/AbstractIdPModule.java
index fc4a87de5..5c1ddef0f 100644
--- a/idp-admin-api/src/main/java/net/shibboleth/idp/module/AbstractIdPModule.java
+++ b/idp-admin-api/src/main/java/net/shibboleth/idp/module/AbstractIdPModule.java
@@ -32,7 +32,9 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -112,25 +114,48 @@ public abstract class AbstractIdPModule implements IdPModule {
}
/** {@inheritDoc} */
- public void enable(@Nonnull final ModuleContext moduleContext) throws ModuleException {
+ @Nonnull @NonnullElements public Map<ModuleResource, ResourceResult> enable(
+ @Nonnull final ModuleContext moduleContext) throws ModuleException {
if (isHttpClientRequired() && moduleContext.getHttpClient() == null) {
throw new ModuleException("HTTP client required but not available");
}
log.debug("Module {} enabling", getId());
- for (final ModuleResource resource : moduleResources) {
- ((BasicModuleResource) resource).enable(moduleContext);
+
+ final Map<ModuleResource,ResourceResult> results;
+
+ if (!moduleResources.isEmpty()) {
+ results = new HashMap<>(moduleResources.size());
+
+ for (final ModuleResource resource : moduleResources) {
+ results.put(resource, ((BasicModuleResource) resource).enable(moduleContext));
+ }
+ } else {
+ results = Collections.emptyMap();
}
- log.info("Module {} enabled", getId());
+
+ log.debug("Module {} enabled", getId());
+ return results;
}
/** {@inheritDoc} */
- public void disable(@Nonnull final ModuleContext moduleContext, final boolean clean) throws ModuleException {
+ @Nonnull @NonnullElements public Map<ModuleResource, ResourceResult> disable(
+ @Nonnull final ModuleContext moduleContext, final boolean clean) throws ModuleException {
log.debug("Module {} disabling", getId());
- for (final ModuleResource resource : moduleResources) {
- ((BasicModuleResource) resource).disable(moduleContext, clean);
+
+ final Map<ModuleResource,ResourceResult> results;
+
+ if (!moduleResources.isEmpty()) {
+ results = new HashMap<>(moduleResources.size());
+ for (final ModuleResource resource : moduleResources) {
+ results.put(resource, ((BasicModuleResource) resource).disable(moduleContext, clean));
+ }
+ } else {
+ results = Collections.emptyMap();
}
- log.info("Module {} disabled", getId());
+
+ log.debug("Module {} disabled", getId());
+ return results;
}
/**
@@ -161,6 +186,20 @@ public abstract class AbstractIdPModule implements IdPModule {
replace = shouldReplace;
}
+ /** {@inheritDoc} */
+ public int hashCode() {
+ return source.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(final Object obj) {
+ if (obj instanceof ModuleResource) {
+ return source.equals(((ModuleResource) obj).getSource()) &&
+ destination.equals(((ModuleResource) obj).getDestination());
+ }
+ return false;
+ }
+
/** {@inheritDoc} */
@Nonnull public String getSource() {
return source;
@@ -312,9 +351,11 @@ public abstract class AbstractIdPModule implements IdPModule {
*
* @param moduleContext module context
*
+ * @return result of operation
+ *
* @throws ModuleException if an error occurs
*/
- private void enable(@Nonnull final ModuleContext moduleContext) throws ModuleException {
+ @Nonnull private ResourceResult enable(@Nonnull final ModuleContext moduleContext) throws ModuleException {
log.debug("Module {} enabling resource {}", getId(), source);
final boolean hasChanged = hasChanged(moduleContext);
@@ -325,20 +366,24 @@ public abstract class AbstractIdPModule implements IdPModule {
}
final Path destPath;
+ final ResourceResult result;
if (hasChanged) {
if (isReplace()) {
destPath = moduleContext.getIdPHome().resolve(destination);
Files.copy(destPath, destPath.resolveSibling(destPath.getFileName() + ".idpsave"),
StandardCopyOption.REPLACE_EXISTING);
- log.info("Module {} preserved {}", getId(), destPath);
+ log.debug("Module {} preserved {}", getId(), destPath);
+ result = ResourceResult.REPLACED;
} else {
final Path basePath = moduleContext.getIdPHome().resolve(destination);
destPath = basePath.resolveSibling(basePath.getFileName() + ".idpnew");
+ result = ResourceResult.ADDED;
}
} else {
destPath = moduleContext.getIdPHome().resolve(destination);
+ result = ResourceResult.CREATED;
}
if (!destPath.startsWith(moduleContext.getIdPHome())) {
@@ -347,8 +392,8 @@ public abstract class AbstractIdPModule implements IdPModule {
}
Files.copy(srcStream, destPath, StandardCopyOption.REPLACE_EXISTING);
- log.info("Module {} created {}", getId(), destPath);
-
+ log.debug("Module {} created {}", getId(), destPath);
+ return result;
} catch (final IOException e) {
log.error("Module {} unable to enable resource {}", getId(), source);
throw new ModuleException(e);
@@ -361,29 +406,38 @@ public abstract class AbstractIdPModule implements IdPModule {
* @param moduleContext module context
* @param clean true iff resource should be removed
*
+ * @return result of operation
+ *
* @throws ModuleException if an error occurs
*/
- private void disable(@Nonnull final ModuleContext moduleContext, final boolean clean) throws ModuleException {
+ @Nonnull private ResourceResult disable(@Nonnull final ModuleContext moduleContext, final boolean clean)
+ throws ModuleException {
+ final ResourceResult result;
final Path resolved = moduleContext.getIdPHome().resolve(destination);
log.debug("Module {} resolved resource destination {}", getId(), resolved);
if (Files.exists(resolved)) {
try {
if (clean || !hasChanged(moduleContext)) {
- log.info("Module {} removing resource {}", getId(), resolved);
+ log.debug("Module {} removing resource {}", getId(), resolved);
Files.delete(resolved);
+ result = ResourceResult.REMOVED;
} else {
- log.info("Module {} backing up resource {}", getId(), resolved);
+ log.debug("Module {} backing up resource {}", getId(), resolved);
Files.move(resolved, resolved.resolveSibling(resolved.getFileName() + ".idpsave"),
StandardCopyOption.REPLACE_EXISTING);
+ result = ResourceResult.SAVED;
}
} catch (final IOException e) {
log.error("Module {} failed to disable {}", getId(), resolved);
throw new ModuleException(e);
}
} else {
- log.info("Module {} resource {} missing, ignoring", getId(), resolved);
+ log.debug("Module {} resource {} missing, ignoring", getId(), resolved);
+ result = ResourceResult.MISSING;
}
+
+ return result;
}
}
diff --git a/idp-admin-api/src/main/java/net/shibboleth/idp/module/IdPModule.java b/idp-admin-api/src/main/java/net/shibboleth/idp/module/IdPModule.java
index f072d5a51..0a78b1501 100644
--- a/idp-admin-api/src/main/java/net/shibboleth/idp/module/IdPModule.java
+++ b/idp-admin-api/src/main/java/net/shibboleth/idp/module/IdPModule.java
@@ -20,6 +20,7 @@ package net.shibboleth.idp.module;
import java.net.URL;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -94,9 +95,12 @@ public interface IdPModule extends IdentifiedComponent {
*
* @param moduleContext module context
*
+ * @return summary of resource results
+ *
* @throws ModuleException if not successful
*/
- void enable(@Nonnull final ModuleContext moduleContext) throws ModuleException;
+ @Nonnull @NonnullElements Map<ModuleResource,ResourceResult> enable(@Nonnull final ModuleContext moduleContext)
+ throws ModuleException;
/**
* Disable the module.
@@ -107,9 +111,12 @@ public interface IdPModule extends IdentifiedComponent {
* @param clean if true, the module should attempt to fully remove traces of previous
* use in a potentially destructive fashion
*
+ * @return summary of resource results
+ *
* @throws ModuleException if not successful
*/
- void disable(@Nonnull final ModuleContext moduleContext, final boolean clean) throws ModuleException;
+ @Nonnull @NonnullElements Map<ModuleResource,ResourceResult> disable(@Nonnull final ModuleContext moduleContext,
+ final boolean clean) throws ModuleException;
/**
* Interface to a resource managed by the module.
@@ -139,5 +146,26 @@ public interface IdPModule extends IdentifiedComponent {
*/
public boolean isReplace();
}
+
+ /** Resource management outcome. */
+ public enum ResourceResult {
+ /** Resource was created. */
+ CREATED,
+
+ /** Resource was created and old resource saved. */
+ REPLACED,
+
+ /** Resource was created alongside original. */
+ ADDED,
+
+ /** Resource was removed. */
+ REMOVED,
+
+ /** Old resource was preserved. */
+ SAVED,
+
+ /** Resource was missing. */
+ MISSING,
+ };
}
\ No newline at end of file
diff --git a/idp-admin-api/src/main/java/net/shibboleth/idp/module/PropertyDrivenIdPModule.java b/idp-admin-api/src/main/java/net/shibboleth/idp/module/PropertyDrivenIdPModule.java
index 87c9c2416..5cbb7e463 100644
--- a/idp-admin-api/src/main/java/net/shibboleth/idp/module/PropertyDrivenIdPModule.java
+++ b/idp-admin-api/src/main/java/net/shibboleth/idp/module/PropertyDrivenIdPModule.java
@@ -24,6 +24,7 @@ import java.net.URL;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Map;
import java.util.Properties;
import javax.annotation.Nonnull;
@@ -32,6 +33,7 @@ import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
@@ -212,8 +214,9 @@ public class PropertyDrivenIdPModule extends AbstractIdPModule {
/** {@inheritDoc} */
@Override
- public void enable(@Nullable final ModuleContext moduleContext) throws ModuleException {
- super.enable(moduleContext);
+ @Nonnull @NonnullElements public Map<ModuleResource,ResourceResult> enable(
+ @Nullable final ModuleContext moduleContext) throws ModuleException {
+ final Map<ModuleResource,ResourceResult> results = super.enable(moduleContext);
if (moduleContext.getMessageStream() != null) {
final String msg = moduleProperties.getProperty(getId() + MODULE_POSTENABLE_PROPERTY);
@@ -221,12 +224,15 @@ public class PropertyDrivenIdPModule extends AbstractIdPModule {
moduleContext.getMessageStream().println(msg);
}
}
+
+ return results;
}
/** {@inheritDoc} */
@Override
- public void disable(@Nullable final ModuleContext moduleContext, final boolean clean) throws ModuleException {
- super.disable(moduleContext, clean);
+ @Nonnull @NonnullElements public Map<ModuleResource,ResourceResult> disable(
+ @Nullable final ModuleContext moduleContext, final boolean clean) throws ModuleException {
+ final Map<ModuleResource,ResourceResult> results = super.disable(moduleContext, clean);
if (moduleContext.getMessageStream() != null) {
final String msg = moduleProperties.getProperty(getId() + MODULE_POSTDISABLE_PROPERTY);
@@ -234,6 +240,8 @@ public class PropertyDrivenIdPModule extends AbstractIdPModule {
moduleContext.getMessageStream().println(msg);
}
}
+
+ return results;
}
}
\ No newline at end of file
diff --git a/idp-admin-impl/.settings/org.eclipse.jdt.core.prefs b/idp-admin-impl/.settings/org.eclipse.jdt.core.prefs
index 7fa508cf8..af08edf91 100644
--- a/idp-admin-impl/.settings/org.eclipse.jdt.core.prefs
+++ b/idp-admin-impl/.settings/org.eclipse.jdt.core.prefs
@@ -132,7 +132,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
diff --git a/idp-admin-impl/src/main/java/net/shibboleth/idp/module/impl/ModuleManagerArguments.java b/idp-admin-impl/src/main/java/net/shibboleth/idp/module/impl/ModuleManagerArguments.java
index f58d30ca7..3a3ba59f2 100644
--- a/idp-admin-impl/src/main/java/net/shibboleth/idp/module/impl/ModuleManagerArguments.java
+++ b/idp-admin-impl/src/main/java/net/shibboleth/idp/module/impl/ModuleManagerArguments.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.http.client.HttpClient;
@@ -75,7 +76,7 @@ public class ModuleManagerArguments extends AbstractIdPHomeAwareCommandLineArgum
@Nullable @NotEmpty private String httpClientSecurityParametersName;
/** {@inheritDoc} */
- public Logger getLog() {
+ @Nonnull public Logger getLog() {
if (log == null) {
log = LoggerFactory.getLogger(ModuleManagerArguments.class);
}
diff --git a/idp-admin-impl/src/main/java/net/shibboleth/idp/module/impl/ModuleManagerCLI.java b/idp-admin-impl/src/main/java/net/shibboleth/idp/module/impl/ModuleManagerCLI.java
index d87b9c34b..379899636 100644
--- a/idp-admin-impl/src/main/java/net/shibboleth/idp/module/impl/ModuleManagerCLI.java
+++ b/idp-admin-impl/src/main/java/net/shibboleth/idp/module/impl/ModuleManagerCLI.java
@@ -17,7 +17,13 @@
package net.shibboleth.idp.module.impl;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.Charset;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.ServiceLoader;
import javax.annotation.Nonnull;
@@ -31,9 +37,13 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
+import com.google.common.base.Strings;
+
import net.shibboleth.idp.Version;
import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLine;
import net.shibboleth.idp.module.IdPModule;
+import net.shibboleth.idp.module.IdPModule.ModuleResource;
+import net.shibboleth.idp.module.IdPModule.ResourceResult;
import net.shibboleth.idp.module.ModuleContext;
import net.shibboleth.idp.module.ModuleException;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
@@ -122,7 +132,7 @@ public final class ModuleManagerCLI extends AbstractIdPHomeAwareCommandLine<Modu
doManage(moduleContext, args);
}
} catch (final ModuleException e) {
- System.out.println("FAILED");
+ System.out.println(ANSIColors.ANSI_RED + "[FAILED]" + ANSIColors.ANSI_RESET);
System.out.println();
return RC_INIT;
}
@@ -144,10 +154,24 @@ public final class ModuleManagerCLI extends AbstractIdPHomeAwareCommandLine<Modu
System.out.println("\tName: " + module.getName());
System.out.println("\tDesc: " + module.getDescription());
System.out.println("\tHelp: " + module.getURL());
- System.out.println("\tStatus: " + (module.isEnabled(moduleContext) ? "ENABLED" : "DISABLED"));
+ if (module.isEnabled(moduleContext)) {
+ System.out.println("\tStatus: " + ANSIColors.ANSI_GREEN + "ENABLED" + ANSIColors.ANSI_RESET);
+ } else {
+ System.out.println("\tStatus: " + ANSIColors.ANSI_RED + "DISABLED" + ANSIColors.ANSI_RESET);
+ }
+ final Collection<ModuleResource> resources = module.getResources();
+ resources.forEach(r -> {
+ System.out.println("\tResource: (" + (r.isReplace() ? " replace" : "noreplace") + ") " +
+ r.getDestination());
+ });
} else {
- System.out.println("Module: " + module.getId() + ": " +
- (module.isEnabled(moduleContext) ? "ENABLED" : "DISABLED"));
+ if (module.isEnabled(moduleContext)) {
+ System.out.println("Module: " + module.getId() +
+ ANSIColors.ANSI_GREEN + " [ENABLED]" + ANSIColors.ANSI_RESET);
+ } else {
+ System.out.println("Module: " + module.getId() +
+ ANSIColors.ANSI_RED + " [DISABLED]" + ANSIColors.ANSI_RESET);
+ }
}
System.out.println();
}
@@ -164,18 +188,77 @@ public final class ModuleManagerCLI extends AbstractIdPHomeAwareCommandLine<Modu
private void doManage(@Nonnull final ModuleContext moduleContext, @Nonnull final ModuleManagerArguments args)
throws ModuleException {
for (final IdPModule module : ServiceLoader.load(IdPModule.class)) {
+
+ final boolean enable;
if (args.getEnableModuleIds().contains(module.getId())) {
- System.out.print("Enabling " + module.getId() + "...");
- module.enable(moduleContext);
- System.out.println("OK");
+ enable = true;
} else if (args.getDisableModuleIds().contains(module.getId())) {
- System.out.print("Disabling " + module.getId() + "...");
- module.disable(moduleContext, args.getClean());
- System.out.println("OK");
+ enable = false;
+ } else {
+ continue;
+ }
+
+ try (final ByteArrayOutputStream sink = new ByteArrayOutputStream()) {
+ System.out.println((enable ? "Enabling " : "Disabling ") + module.getId() + "...");
+ moduleContext.setMessageStream(new PrintStream(sink));
+
+ final Map<ModuleResource,ResourceResult> results = enable ? module.enable(moduleContext) :
+ module.disable(moduleContext, args.getClean());
+ results.forEach(this::doReportOperation);
+
+ System.out.println(ANSIColors.ANSI_GREEN + "[OK]" + ANSIColors.ANSI_RESET);
+ System.out.println();
+
+ final String msg = sink.toString(Charset.forName("UTF-8"));
+ moduleContext.setMessageStream(null);
+ if (!Strings.isNullOrEmpty(msg)) {
+ System.out.println(msg);
+ System.out.println();
+ }
+
+ } catch (final IOException e) {
+ getLogger().error("I/O Error", e);
}
}
}
+ /**
+ * Report on a resource result.
+ *
+ * @param resource resource
+ * @param result result of operation
+ */
+ private void doReportOperation(@Nonnull final ModuleResource resource, @Nonnull final ResourceResult result) {
+ System.out.print("\t" + resource.getDestination());
+ switch (result) {
+ case CREATED:
+ System.out.println(" created");
+ break;
+
+ case REPLACED:
+ System.out.println(" replaced, " + resource.getDestination() + ".idpsave created");
+ break;
+
+ case ADDED:
+ System.out.println(".idpnew created");
+ break;
+
+ case REMOVED:
+ System.out.println(" removed");
+ break;
+
+ case SAVED:
+ System.out.println(" renamed to " + resource.getDestination() + ".idpsave");
+ break;
+
+ case MISSING:
+ System.out.println(" missing, nothing to do");
+ break;
+
+ default:
+ }
+ }
+
/**
* CLI entry point.
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list