Skip to content

Commit

Permalink
#18 Provide a settings console instead of the OSGi config - threads p…
Browse files Browse the repository at this point in the history
…er core
  • Loading branch information
vihnatsenka committed Mar 19, 2024
1 parent b7bcc08 commit 5a49ccf
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ public interface UiConfigService {
String getLinksType();
boolean isExcludeTags();
Integer[] getStatusCodes();
Integer getThreadsPerCore();
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import com.exadel.etoolbox.linkinspector.core.services.data.models.GridResource;
import com.exadel.etoolbox.linkinspector.core.services.util.LinkInspectorResourceUtil;
import com.exadel.etoolbox.linkinspector.core.services.util.LinksCounter;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
Expand All @@ -35,14 +34,9 @@
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.jcr.resource.api.JcrResourceConstants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -65,24 +59,9 @@
* generation and further adaptation the data feed to the models for building the UI grid
*/
@Component(service = GridResourcesGenerator.class)
@Designate(ocd = GridResourcesGeneratorImpl.Configuration.class)
public class GridResourcesGeneratorImpl implements GridResourcesGenerator {
@ObjectClassDefinition(
name = "EToolbox Link Inspector - Grid Resources Generator",
description = "Finds broken links under the specified path for further outputting them in a report"
)
@interface Configuration {
@AttributeDefinition(
name = "Threads per core",
description = "The number of threads created per each CPU core for validating links in parallel"
) int threadsPerCore() default DEFAULT_THREADS_PER_CORE;
}

private static final Logger LOG = LoggerFactory.getLogger(GridResourcesGeneratorImpl.class);

private static final String DEFAULT_SEARCH_PATH = "/content";
private static final int DEFAULT_THREADS_PER_CORE = 60;

private static final String TAGS_LOCATION = "/content/cq:tags";
private static final String STATS_RESOURCE_PATH = "/content/etoolbox-link-inspector/data/stats";

Expand All @@ -93,18 +72,6 @@ public class GridResourcesGeneratorImpl implements GridResourcesGenerator {

private ExecutorService executorService;

private int threadsPerCore;

/**
* Inits fields based on the service configuration
* @param configuration - the service configuration
*/
@Activate
@Modified
protected void activate(Configuration configuration) {
threadsPerCore = configuration.threadsPerCore();
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -202,7 +169,7 @@ private Set<GridResource> validateLinksInParallel(Map<Link, List<GridResource>>
Set<GridResource> allBrokenLinkResources = new CopyOnWriteArraySet<>();
try {
executorService =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * threadsPerCore);
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * uiConfigService.getThreadsPerCore());
linkToGridResourcesMap.forEach((link, resources) ->
submitLinkForValidation(
link,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public class UiConfigServiceImpl implements UiConfigService {
private static final String PN_LINKS_TYPE = "linksType";
private static final String PN_EXCLUDE_TAGS = "excludeTags";
private static final String PN_STATUS_CODES = "statusCodes";
private static final String PN_THREADS_PER_CORE = "threadsPerCore";
private static final int DEFAULT_THREADS_PER_CORE = 60;

private static final String DEFAULT_PATH = "/content";

@Reference
Expand Down Expand Up @@ -84,6 +87,11 @@ public Integer[] getStatusCodes() {
return getProperty(PN_STATUS_CODES, Integer[].class).orElse(new Integer[]{});
}

@Override
public Integer getThreadsPerCore() {
return getProperty(PN_THREADS_PER_CORE, Integer.class).orElse(DEFAULT_THREADS_PER_CORE);
}

private <T> Optional<T> getProperty(String name, Class<T> clazz){
try(ResourceResolver resourceResolver = repositoryHelper.getServiceResourceResolver()){
return Optional.ofNullable(resourceResolver.getResource(CONFIG_PATH))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ private GridResourcesGeneratorImpl getGridResourcesGenerator() throws NoSuchFiel
when(uiConfigService.getExcludedProperties()).thenReturn(new String[0]);
when(uiConfigService.getLinksType()).thenReturn(GenerationStatsProps.REPORT_LINKS_TYPE_ALL);
when(uiConfigService.getStatusCodes()).thenReturn(new Integer[]{HttpStatus.SC_NOT_FOUND});
when(uiConfigService.getThreadsPerCore()).thenReturn(60);
PrivateAccessor.setField(gridResourcesGenerator, UI_CONFIG_FIELD, uiConfigService);
GridResourcesGeneratorImplTest.setUpConfig(gridResourcesGenerator);

when(externalLinkChecker.checkLink(anyString())).thenReturn(HttpStatus.SC_NOT_FOUND);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ void setup() throws NoSuchFieldException {
when(uiConfigService.getLinksType()).thenReturn(GenerationStatsProps.REPORT_LINKS_TYPE_ALL);
when(uiConfigService.isExcludeTags()).thenReturn(true);
when(uiConfigService.getStatusCodes()).thenReturn(new Integer[]{HttpStatus.SC_NOT_FOUND});
when(uiConfigService.getThreadsPerCore()).thenReturn(60);
PrivateAccessor.setField(fixture, UI_CONFIG_FIELD, uiConfigService);
}

@Test
void testGenerateGridResources() throws NoSuchFieldException, IOException, URISyntaxException {
setUpConfig(fixture);
context.load().json(TEST_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);
when(externalLinkChecker.checkLink(anyString())).thenReturn(HttpStatus.SC_NOT_FOUND);

Expand All @@ -166,7 +166,6 @@ void testGenerateGridResources() throws NoSuchFieldException, IOException, URISy

@Test
void testGenerateFilteredGridResources() throws NoSuchFieldException, IOException, URISyntaxException {
setUpConfig(fixture);
context.load().json(TEST_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);
when(externalLinkChecker.checkLink(anyString())).thenReturn(HttpStatus.SC_NOT_FOUND);
when(uiConfigService.getExcludedLinksPatterns()).thenReturn(new String[]{TEST_UI_EXCLUDED_PATTERN, TEST_EXCLUDED_PATTERN});
Expand All @@ -185,7 +184,6 @@ void testGenerateFilteredGridResources() throws NoSuchFieldException, IOExceptio

@Test
void testAllowedStatusCodes() throws IOException, URISyntaxException {
setUpConfig(fixture);
context.load().json(TEST_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);
when(externalLinkChecker.checkLink(anyString())).thenReturn(HttpStatus.SC_BAD_REQUEST);

Expand All @@ -200,7 +198,7 @@ void testAllowedStatusCodes() throws IOException, URISyntaxException {

@Test
void testAllowedStatusCodes_emptyConfig() throws IOException, URISyntaxException, NoSuchFieldException {
setUpConfigNoStatusCodes(fixture);
when(uiConfigService.getStatusCodes()).thenReturn(new Integer[]{});

context.load().json(TEST_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);
when(externalLinkChecker.checkLink(anyString())).thenReturn(HttpStatus.SC_BAD_REQUEST);
Expand All @@ -213,7 +211,6 @@ void testAllowedStatusCodes_emptyConfig() throws IOException, URISyntaxException

@Test
void testExcludedPaths() throws IOException, URISyntaxException {
setUpConfig(fixture);
context.load().json(TEST_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);
when(externalLinkChecker.checkLink(anyString())).thenReturn(HttpStatus.SC_BAD_REQUEST);

Expand All @@ -229,7 +226,6 @@ void testExcludedPaths() throws IOException, URISyntaxException {

@Test
void testLastModified() {
setUpConfig(fixture);
context.load().json(TEST_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);

List<GridResource> gridResources = fixture.generateGridResources(GRID_RESOURCE_TYPE, context.resourceResolver());
Expand All @@ -242,7 +238,7 @@ void testLastModified() {

@Test
void testExcludedPaths_emptyConfig() throws IOException, URISyntaxException {
setUpConfigNoExcludedPaths(fixture);
when(uiConfigService.getExcludedPaths()).thenReturn(ArrayUtils.EMPTY_STRING_ARRAY);
context.load().json(TEST_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);
when(externalLinkChecker.checkLink(anyString())).thenReturn(HttpStatus.SC_BAD_REQUEST);

Expand All @@ -260,7 +256,9 @@ void testExcludedPaths_emptyConfig() throws IOException, URISyntaxException {

@Test
void testActivationCheck() throws ParseException {
setUpConfigCheckActivation(fixture);
when(uiConfigService.getExcludedPaths()).thenReturn(new String[]{TEST_EXCLUDED_PATH});
when(uiConfigService.isActivatedContent()).thenReturn(true);
when(uiConfigService.isSkipContentModifiedAfterActivation()).thenReturn(true);
context.load().json(TEST_REPLICATED_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);

Resource rootResource = context.resourceResolver().getResource(TEST_FOLDER_PATH);
Expand All @@ -286,14 +284,12 @@ void testActivationCheck() throws ParseException {

@Test
void testGenerateGridResources_rootResourceNull() {
setUpConfig(fixture);
List<GridResource> gridResources = fixture.generateGridResources(GRID_RESOURCE_TYPE, context.resourceResolver());
assertTrue(gridResources.isEmpty());
}

@Test
void testGenerateGridResources_nothingFoundAfterTraversing() {
setUpConfig(fixture);
context.create().resource(TEST_FOLDER_PATH,
JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, JcrResourceConstants.NT_SLING_FOLDER);

Expand All @@ -313,7 +309,6 @@ void testDeactivate() throws NoSuchFieldException {

@Test
void testGenerateGridResources_interruptionException() throws InterruptedException {
setUpConfig(fixture);
context.load().json(TEST_RESOURCES_TREE_PATH, TEST_FOLDER_PATH);

try (MockedStatic<Executors> executors = mockStatic(Executors.class)) {
Expand All @@ -328,37 +323,6 @@ void testGenerateGridResources_interruptionException() throws InterruptedExcepti
}
}

static void setUpConfig(GridResourcesGeneratorImpl gridResourcesGenerator) {
GridResourcesGeneratorImpl.Configuration config = mockConfig();
gridResourcesGenerator.activate(config);
}

private void setUpConfigNoExcludedPaths(GridResourcesGeneratorImpl gridResourcesGenerator) {
GridResourcesGeneratorImpl.Configuration config = mockConfig();
when(uiConfigService.getExcludedPaths()).thenReturn(ArrayUtils.EMPTY_STRING_ARRAY);
gridResourcesGenerator.activate(config);
}

private void setUpConfigCheckActivation(GridResourcesGeneratorImpl gridResourcesGenerator) {
GridResourcesGeneratorImpl.Configuration config = mockConfig();
when(uiConfigService.getExcludedPaths()).thenReturn(new String[]{TEST_EXCLUDED_PATH});
when(uiConfigService.isActivatedContent()).thenReturn(true);
when(uiConfigService.isSkipContentModifiedAfterActivation()).thenReturn(true);
gridResourcesGenerator.activate(config);
}

private void setUpConfigNoStatusCodes(GridResourcesGeneratorImpl gridResourcesGenerator) {
GridResourcesGeneratorImpl.Configuration config = mockConfig();
when(uiConfigService.getStatusCodes()).thenReturn(new Integer[]{});
gridResourcesGenerator.activate(config);
}

private static GridResourcesGeneratorImpl.Configuration mockConfig() {
GridResourcesGeneratorImpl.Configuration config = mock(GridResourcesGeneratorImpl.Configuration.class);
when(config.threadsPerCore()).thenReturn(60);
return config;
}

private List<GridResource> buildExpectedGridResources() throws NoSuchFieldException {
context.load().binaryFile(TEST_DATAFEED_PATH, REAL_DATAFEED_PATH);
DataFeedService dataFeedService = setUpDataFeedService(getRepositoryHelperFromContext());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ tr.elc-card {
min-width: 50rem;
}
.coral3-Multifield{
min-width: 48rem;
min-width: 100%;
}
.coral3-Checkbox{
display: block;
}
.coral3-Select{
min-width: 48rem;
min-width: 100%;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@
$cancelBtn.appendTo(dialog.footer);
$updateBtn.appendTo(dialog.footer);

const filterMultifield = createMultifield();
$('<p>').text("Filter").appendTo(dialog.content);
dialog.content.appendChild(filterMultifield);

const $rootPathField = $('<input is="coral-textfield" class="elc-replacement-input" name="replacement" value="" required>');
const $rootPathField = $('<input is="coral-textfield" class="elc-replacement-input" name="replacement" value="">');
$('<p>').text("Path(The content path for searching broken links. The search path should be located under /content)").appendTo(dialog.content);
$rootPathField.appendTo(dialog.content);

Expand All @@ -78,7 +74,11 @@
const $skipContentAfterActivationCheckbox = $('<coral-checkbox value="skipContentAfterActivation">Skip content modified after activation(Works in conjunction with the \'Activated Content\' checkbox only. If checked, links will be retrieved from activated content that is not modified after activation (lastModified is before lastReplicated))</coral-checkbox>');
$skipContentAfterActivationCheckbox.appendTo(dialog.content);

const $lastModifiedContentField = $('<input is="coral-textfield" class="elc-replacement-input" name="lastMidified" value="" required>');
const filterMultifield = createMultifield();
$('<p>').text("Excluded links patterns(Links are excluded from processing if match any of the specified regex patterns)").appendTo(dialog.content);
dialog.content.appendChild(filterMultifield);

const $lastModifiedContentField = $('<input is="coral-textfield" class="elc-replacement-input" name="lastMidified" value="">');
$('<p>').text("Last Modified (The content modified before the specified date will be excluded. Tha date should has the ISO-like date-time format, such as '2011-12-03T10:15:30+01:00')").appendTo(dialog.content);
$lastModifiedContentField.appendTo(dialog.content);

Expand Down Expand Up @@ -120,6 +120,10 @@
$('<p>').text("Status codes (The list of status codes allowed for broken links in the report. Set a single negative value to allow all http error codes)").appendTo(dialog.content);
dialog.content.appendChild(statusCodesMultifield);

const $threadsPerCoreField = $('<input is="coral-textfield" class="elc-replacement-input" name="threadsPerCore" value="">');
$('<p>').text("Threads per core (The number of threads created per each CPU core for validating links in parallel)").appendTo(dialog.content);
$threadsPerCoreField.appendTo(dialog.content);

$.ajax({
type: "GET",
url: "/content/etoolbox-link-inspector/data/config.json"
Expand All @@ -134,6 +138,7 @@
linksTypeSelect.value = data.linksType;
$excludeTagsCheckbox.attr("checked", data.excludeTags);
populateMultifield(statusCodesMultifield, data.statusCodes);
$threadsPerCoreField.val(data.threadsPerCore);
})

function createMultifield(){
Expand Down Expand Up @@ -188,6 +193,7 @@
"excludeTags@TypeHint": "Boolean",
"statusCodes": getMultifieldValues(statusCodesMultifield),
"statusCodes@TypeHint": "String[]",
"threadsPerCore": $threadsPerCoreField.val()
},
dataType: "json",
encode: true
Expand Down
1 change: 1 addition & 0 deletions ui.content/src/main/content/META-INF/vault/filter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
<filter root="/content/rep:policy" mode="merge"/>
<filter root="/content/etoolbox-link-inspector/link-inspector" mode="replace"/>
<filter root="/content/etoolbox-link-inspector" mode="merge"/>
<filter root="/content/etoolbox-link-inspector/data/config" mode="merge"/>
</workspaceFilter>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="nt:unstructured"
path="/content"
excludedProperties="[dam:Comments,cq:allowedTemplates,cq:childrenOrder,cq:designPath,cq:lastModifiedBy,cq:lastPublishedBy,cq:lastReplicatedBy,cq:lastReplicationAction,cq:lastReplicationStatus,cq:lastRolledoutBy,cq:template,jcr:createdBy,sling:resourceType,sling:resourceSuperType]"
linksType="Internal + External"
excludeTags="{Boolean}true"
statusCodes="[404]"
threadsPerCore="60">
</jcr:root>

0 comments on commit 5a49ccf

Please sign in to comment.