Skip to content

Commit

Permalink
Merge 68da222 into 7e46715
Browse files Browse the repository at this point in the history
  • Loading branch information
YegorKozlov committed May 9, 2021
2 parents 7e46715 + 68da222 commit 0c79fc4
Show file tree
Hide file tree
Showing 37 changed files with 1,563 additions and 419 deletions.
3 changes: 2 additions & 1 deletion bundle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@
com.adobe.acs.commons.mcp.model,
com.adobe.acs.commons.reports.models,
com.adobe.acs.commons.indesign.dynamicdeckdynamo.models,
com.adobe.acs.commons.sorter
com.adobe.acs.commons.sorter,
com.adobe.acs.commons.redirects.models
</Sling-Model-Packages>
</instructions>
</configuration>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,23 @@

import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.TabularData;
import java.util.Collection;

@Description("ACS Redirect Manager MBean")
public interface RedirectFilterMBean {

@Description("Refreshes the cache of registered configurations")
void refreshCache();
@Description("Invalidate all cached rules")
void invalidateAll();

@Description("Loaded redirect configurations")
TabularData getRedirectConfigurations() throws OpenDataException;
@Description("Loaded redirect rules")
TabularData getRedirectRules(String storagePath) throws OpenDataException;

@Description("Known redirect configurations")
Collection<String> getRedirectConfigurations();

@Description("Configuration bucket to store redirects")
String getBucket();

@Description("Node name to store redirect configurations")
String getConfigName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2016 Adobe
* %%
* 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.
* #L%
*/
@org.osgi.annotation.versioning.Version("6.0.0")
package com.adobe.acs.commons.redirects.filter;
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2016 Adobe
* %%
* 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.
* #L%
*/
package com.adobe.acs.commons.redirects.models;

import com.adobe.acs.commons.redirects.filter.RedirectFilterMBean;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.query.Query;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
* A Sling model to list available redirect configurations on
* /apps/acs-commons/content/redirect-manager/redirects.html
*/
@Model(adaptables = SlingHttpServletRequest.class)
public class Configurations {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

@SlingObject
private SlingHttpServletRequest request;

@OSGiService
private RedirectFilterMBean redirectFilter;

private static final String REDIRECTS_RESOURCE_TYPE = "acs-commons/components/utilities/manage-redirects/redirects";

public Collection<RedirectConfiguration> getConfigurations() {
String sql = "SELECT * FROM [nt:unstructured] AS s WHERE ISDESCENDANTNODE([/conf]) "
+ "AND s.[sling:resourceType]='" + REDIRECTS_RESOURCE_TYPE + "'";
log.debug(sql);
Iterator<Resource> it = request.getResourceResolver().findResources(sql, Query.JCR_SQL2);
List<RedirectConfiguration> lst = new ArrayList<>();
while (it.hasNext()) {
Resource resource = it.next();
String storageSuffix = redirectFilter.getBucket() + "/" + redirectFilter.getConfigName();
RedirectConfiguration cfg = new RedirectConfiguration(resource, storageSuffix);
lst.add(cfg);
}
lst.sort(Comparator.comparing(RedirectConfiguration::getName));
return lst;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2016 Adobe
* %%
* 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.
* #L%
*/
package com.adobe.acs.commons.redirects.models;

import com.adobe.acs.commons.redirects.filter.RedirectFilter;
import org.apache.sling.api.resource.Resource;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* A collection of redirect rules
*/
public class RedirectConfiguration {
/**
* path rules keyed by source, e.g. path1 -> path2.
* This makes lookup by path a O(1) operation
*/
private Map<String, RedirectRule> pathRules;
/**
* regex rules keyed by their regex pattern.
*/
private Map<Pattern, RedirectRule> patternRules;
private String path;
private String name;

public static final RedirectConfiguration EMPTY = new RedirectConfiguration();

private RedirectConfiguration(){
pathRules = new LinkedHashMap<>();
patternRules = new LinkedHashMap<>();
}

public RedirectConfiguration(Resource resource, String storageSuffix) {
pathRules = new LinkedHashMap<>();
patternRules = new LinkedHashMap<>();
path = resource.getPath();
name = path.replace("/" + storageSuffix, "");
Collection<RedirectRule> rules = RedirectFilter.getRules(resource);
for (RedirectRule rule : rules) {
if (rule.getRegex() != null) {
patternRules.put(rule.getRegex(), rule);
} else {
pathRules.put(normalizePath(rule.getSource()), rule);
}
}

}

/**
* @return resource path without .html extension
*/

public static String normalizePath(String resourcePath) {
int sep = resourcePath.lastIndexOf('.');
if (sep != -1 && !resourcePath.startsWith("/content/dam/")) {
// strip off extension if present
resourcePath = resourcePath.substring(0, sep);
}
return resourcePath;
}

public Map<String, RedirectRule> getPathRules() {
return pathRules;
}

public Map<Pattern, RedirectRule> getPatternRules() {
return patternRules;
}

public String getPath() {
return path;
}

public String getName() {
return name;
}

/**
* Match a request path to a redirect configuration
* Performs two tries:
* <ol>
* <li>Match by exact path. This is O(1) lookup in a hashtable keyed by path</li>
* <li>Match by a regular expression. This is O(N) linear lookup in a list of rules keyed by their regex patterns</li>
* </ol>
*
* @param requestPath the request to match
* @return match or null
*/
public RedirectMatch match(String requestPath) {
String normalizedPath = normalizePath(requestPath);
RedirectMatch match = null;
RedirectRule rule = getPathRules().get(normalizedPath);
if (rule != null) {
match = new RedirectMatch(rule, null);
} else {
for (Map.Entry<Pattern, RedirectRule> entry : getPatternRules().entrySet()) {
Matcher m = entry.getKey().matcher(normalizedPath);
if (m.matches()) {
match = new RedirectMatch(entry.getValue(), m);
break;
}
}
}
return match;

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
*/
package com.adobe.acs.commons.redirects.models;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Model;
Expand All @@ -28,11 +27,9 @@

import javax.inject.Inject;
import java.lang.invoke.MethodHandles;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Calendar;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
Expand All @@ -45,7 +42,7 @@ public class RedirectRule {
public static final String TARGET_PROPERTY_NAME = "target";
public static final String STATUS_CODE_PROPERTY_NAME = "statusCode";
public static final String UNTIL_DATE_PROPERTY_NAME = "untilDate";
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy");
public static final String NOTE_PROPERTY_NAME = "note";

@Inject
private String source;
Expand All @@ -56,31 +53,45 @@ public class RedirectRule {
@Inject
private int statusCode;

private String untilDate;
@Inject
private String note;

private ZonedDateTime untilDate;

private Pattern ptrn;

private SubstitutionElement[] substitutions;

public RedirectRule(ValueMap resource) {
this(resource.get(SOURCE_PROPERTY_NAME, ""), resource.get(TARGET_PROPERTY_NAME, ""),
resource.get(STATUS_CODE_PROPERTY_NAME, 0), resource.get(UNTIL_DATE_PROPERTY_NAME, String.class));
}

public RedirectRule(String source, String target, int statusCode, String untilStr) {
public RedirectRule(String source, String target, int statusCode, Calendar calendar, String note) {
this.source = source.trim();
this.target = target.trim();
this.statusCode = statusCode;
this.note = note;

String regex = this.source;
if (regex.endsWith("*")) {
regex = regex.replaceAll("\\*$", "(.*)");
}
ptrn = toRegex(regex);
substitutions = SubstitutionElement.parse(this.target);
if (StringUtils.isNotBlank(untilStr)) {
untilDate = untilStr.trim();
if (calendar != null) {
untilDate = ZonedDateTime.ofInstant( calendar.toInstant(), calendar.getTimeZone().toZoneId());
}
}

public static RedirectRule from(ValueMap resource) {
String source = resource.get(SOURCE_PROPERTY_NAME, "");
String target = resource.get(TARGET_PROPERTY_NAME, "");
String note = resource.get(NOTE_PROPERTY_NAME, "");
int statusCode = resource.get(STATUS_CODE_PROPERTY_NAME, 0);
Calendar calendar = null;
if(resource.containsKey(UNTIL_DATE_PROPERTY_NAME)){
Object o = resource.get(UNTIL_DATE_PROPERTY_NAME);
if(o instanceof Calendar) {
calendar = (Calendar)o;
}
}
return new RedirectRule(source, target, statusCode, calendar, note);
}

public String getSource() {
Expand All @@ -91,6 +102,10 @@ public String getTarget() {
return target;
}

public String getNote() {
return note;
}

public int getStatusCode() {
return statusCode;
}
Expand All @@ -99,27 +114,14 @@ public Pattern getRegex() {
return ptrn;
}

public String getUntilDate() {
public ZonedDateTime getUntilDate() {
return untilDate;
}

public ZonedDateTime getUntilDateTime() {
if (untilDate != null && !untilDate.isEmpty()) {
try {
LocalDate ld = DATE_FORMATTER.parse(untilDate).query(LocalDate::from);
return ld == null ? null : ld.atStartOfDay().plusDays(1).minusSeconds(1).atZone(ZoneId.systemDefault());
} catch (DateTimeParseException e){
// not fatal. log and continue
log.error("Invalid UntilDateTime {}", untilDate, e);
}
}
return null;
}

@Override
public String toString() {
return String.format("RedirectRule{source='%s', target='%s', statusCode=%s, untilDate=%s}",
source, target, statusCode, untilDate);
return String.format("RedirectRule{source='%s', target='%s', statusCode=%s, untilDate=%s, note=%s}",
source, target, statusCode, untilDate, note);
}

@Override
Expand All @@ -132,7 +134,7 @@ public boolean equals(Object o) {
}
RedirectRule that = (RedirectRule) o;

return source != null ? source.equals(that.source) : that.source == null;
return Objects.equals(source, that.source);
}

@Override
Expand Down

0 comments on commit 0c79fc4

Please sign in to comment.