MNT-24346 Fix "0" return status code when SORL unavailable (#2942)

* MNT-24346: Fix 0 status code in Search API when Solr is unavailable.

* MNT-24346: Fixing PMD issues.

* MNT-24346: Fixing formatting.

* MNT-24346: Reverting accidental method name change.

* MNT-24346: Fixing spotless issues.

* MNT-24346: Fixing spotless issues.

* MNT-24346: Fixing spotless issues.

* MNT-24346 Fix line endings.

---------

Co-authored-by: Tom Page <tom.page@alfresco.com>
This commit is contained in:
Maciej Pichura
2024-09-25 15:27:06 +02:00
committed by GitHub
parent 837fb0cccd
commit 0f6950a72e
2 changed files with 186 additions and 186 deletions

View File

@@ -2,23 +2,23 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
@@ -30,11 +30,8 @@ import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.List; import java.util.List;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.alfresco.httpclient.HttpClientException;
import org.alfresco.repo.search.QueryParserException;
import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.HttpStatus;
@@ -48,13 +45,16 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONTokener; import org.json.JSONTokener;
import org.alfresco.httpclient.HttpClientException;
import org.alfresco.repo.search.QueryParserException;
public abstract class AbstractSolrQueryHTTPClient public abstract class AbstractSolrQueryHTTPClient
{ {
/** Logger for the class. */ /** Logger for the class. */
private static final Log LOGGER = LogFactory.getLog(AbstractSolrQueryHTTPClient.class); private static final Log LOGGER = LogFactory.getLog(AbstractSolrQueryHTTPClient.class);
public static final int DEFAULT_SAVEPOST_BUFFER = 4096; public static final int DEFAULT_SAVEPOST_BUFFER = 4096;
// Constants copied from org.apache.solr.common.params.HighlightParams (solr-solrj:1.4.1) // Constants copied from org.apache.solr.common.params.HighlightParams (solr-solrj:1.4.1)
// These values have been moved to this Alfresco class to avoid using solr-solrj library as dependency // These values have been moved to this Alfresco class to avoid using solr-solrj library as dependency
public static final String HIGHLIGHT_PARAMS_HIGHLIGHT = "hl"; public static final String HIGHLIGHT_PARAMS_HIGHLIGHT = "hl";
@@ -86,7 +86,7 @@ public abstract class AbstractSolrQueryHTTPClient
/** List of SOLR Exceptions that should be returning HTTP 501 status code in Remote API. */ /** List of SOLR Exceptions that should be returning HTTP 501 status code in Remote API. */
private static final List<String> STATUS_CODE_501_EXCEPTIONS = List.of("java.lang.UnsupportedOperationException"); private static final List<String> STATUS_CODE_501_EXCEPTIONS = List.of("java.lang.UnsupportedOperationException");
protected JSONObject postQuery(HttpClient httpClient, String url, JSONObject body) throws IOException, JSONException protected JSONObject postQuery(HttpClient httpClient, String url, JSONObject body) throws IOException, JSONException
{ {
PostMethod post = createNewPostMethod(url); PostMethod post = createNewPostMethod(url);
@@ -99,7 +99,7 @@ public abstract class AbstractSolrQueryHTTPClient
try try
{ {
httpClient.executeMethod(post); httpClient.executeMethod(post);
if(post.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY || post.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY) if (post.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY || post.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY)
{ {
Header locationHeader = post.getResponseHeader("location"); Header locationHeader = post.getResponseHeader("location");
if (locationHeader != null) if (locationHeader != null)

View File

@@ -1,173 +1,173 @@
/* /*
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.search.impl.solr; package org.alfresco.repo.search.impl.solr;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks; import static org.mockito.MockitoAnnotations.openMocks;
import jakarta.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream;
import java.io.ByteArrayInputStream; import java.io.IOException;
import java.io.IOException; import jakarta.servlet.http.HttpServletResponse;
import org.alfresco.httpclient.HttpClientException; import org.apache.commons.httpclient.Header;
import org.alfresco.repo.search.QueryParserException; import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.URI; import org.json.JSONObject;
import org.apache.commons.httpclient.methods.PostMethod; import org.junit.Before;
import org.json.JSONObject; import org.junit.Test;
import org.junit.Before; import org.mockito.Mock;
import org.junit.Test;
import org.mockito.Mock; import org.alfresco.httpclient.HttpClientException;
import org.alfresco.repo.search.QueryParserException;
/** Tests for the {@link AbstractSolrQueryHTTPClient}. */
public class AbstractSolrQueryHTTPClientTest /** Tests for the {@link AbstractSolrQueryHTTPClient}. */
{ public class AbstractSolrQueryHTTPClientTest
/** A URL for use in the tests. */ {
private static final String URL = "http://this/is/a/url"; /** A URL for use in the tests. */
private static final String URL = "http://this/is/a/url";
/** The abstract class under test. */
private final AbstractSolrQueryHTTPClient abstractSolrQueryHTTPClient = spy(AbstractSolrQueryHTTPClient.class); /** The abstract class under test. */
@Mock private final AbstractSolrQueryHTTPClient abstractSolrQueryHTTPClient = spy(AbstractSolrQueryHTTPClient.class);
private HttpClient httpClient; @Mock
@Mock private HttpClient httpClient;
private JSONObject body; @Mock
@Mock private JSONObject body;
private PostMethod postMethod; @Mock
@Mock private PostMethod postMethod;
private Header header; @Mock
private Header header;
@Before
public void setUp() throws Exception @Before
{ public void setUp() throws Exception
openMocks(this); {
openMocks(this);
doReturn(postMethod).when(abstractSolrQueryHTTPClient).createNewPostMethod(URL);
when(postMethod.getResponseCharSet()).thenReturn("UTF-8"); doReturn(postMethod).when(abstractSolrQueryHTTPClient).createNewPostMethod(URL);
} when(postMethod.getResponseCharSet()).thenReturn("UTF-8");
}
/** Check postQuery works as expected for the success case. */
@Test /** Check postQuery works as expected for the success case. */
public void testPostQuery_success() throws Exception @Test
{ public void testPostQuery_success() throws Exception
when(body.toString()).thenReturn("Example body"); {
when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_OK); when(body.toString()).thenReturn("Example body");
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream("{}")); when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_OK);
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream("{}"));
JSONObject response = abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
JSONObject response = abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
assertEquals("Unexpected JSON response received.", "{}", response.toString());
} assertEquals("Unexpected JSON response received.", "{}", response.toString());
}
/** Check that the status code is usually passed through from Solr. */
@Test /** Check that the status code is usually passed through from Solr. */
public void testPostQuery_failure() throws Exception @Test
{ public void testPostQuery_failure() throws Exception
String failureMessage = "{\"error\": {\"trace\": \"ExceptionClass: Stacktrace\"}}"; {
String failureMessage = "{\"error\": {\"trace\": \"ExceptionClass: Stacktrace\"}}";
when(body.toString()).thenReturn("Example body");
when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_NOT_FOUND); when(body.toString()).thenReturn("Example body");
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream(failureMessage)); when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_NOT_FOUND);
when(postMethod.getResponseBodyAsString()).thenReturn(failureMessage); when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream(failureMessage));
when(postMethod.getResponseBodyAsString()).thenReturn(failureMessage);
try
{ try
abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body); {
fail("Expected a QueryParserException to be thrown."); abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
} fail("Expected a QueryParserException to be thrown.");
catch (QueryParserException e) }
{ catch (QueryParserException e)
assertEquals("Unexpected status code in exception.", e.getHttpStatusCode(), HttpServletResponse.SC_NOT_FOUND); {
verify(postMethod).releaseConnection(); assertEquals("Unexpected status code in exception.", e.getHttpStatusCode(), HttpServletResponse.SC_NOT_FOUND);
} verify(postMethod).releaseConnection();
} }
}
/** Check that the status code is replaced with "Not Implemented" for an unsupported query option. */
@Test /** Check that the status code is replaced with "Not Implemented" for an unsupported query option. */
public void testPostQuery_unsupportedOperation() throws Exception @Test
{ public void testPostQuery_unsupportedOperation() throws Exception
String failureMessage = "{\"error\": {\"trace\": \"java.lang.UnsupportedOperationException: Stacktrace\"}}"; {
String failureMessage = "{\"error\": {\"trace\": \"java.lang.UnsupportedOperationException: Stacktrace\"}}";
when(body.toString()).thenReturn("Example body");
when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); when(body.toString()).thenReturn("Example body");
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream(failureMessage)); when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
when(postMethod.getResponseBodyAsString()).thenReturn(failureMessage); when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream(failureMessage));
when(postMethod.getResponseBodyAsString()).thenReturn(failureMessage);
try
{ try
abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body); {
fail("Expected a QueryParserException to be thrown."); abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
} fail("Expected a QueryParserException to be thrown.");
catch (QueryParserException e) }
{ catch (QueryParserException e)
assertEquals("Unexpected status code in exception.", e.getHttpStatusCode(), HttpServletResponse.SC_NOT_IMPLEMENTED); {
} assertEquals("Unexpected status code in exception.", e.getHttpStatusCode(), HttpServletResponse.SC_NOT_IMPLEMENTED);
} }
}
/** Check that a redirect can be followed if the endpoint reports that it's moved. */
@Test /** Check that a redirect can be followed if the endpoint reports that it's moved. */
public void testPostQuery_moved() throws Exception @Test
{ public void testPostQuery_moved() throws Exception
when(body.toString()).thenReturn("Example body"); {
// Report "moved" for the first invocation and then OK for subsequent requests. when(body.toString()).thenReturn("Example body");
when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_MOVED_PERMANENTLY).thenReturn(HttpServletResponse.SC_OK); // Report "moved" for the first invocation and then OK for subsequent requests.
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream("{}")); when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_MOVED_PERMANENTLY).thenReturn(HttpServletResponse.SC_OK);
when(postMethod.getResponseHeader("location")).thenReturn(header); when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream("{}"));
when(header.getValue()).thenReturn("http://new/URL"); when(postMethod.getResponseHeader("location")).thenReturn(header);
when(header.getValue()).thenReturn("http://new/URL");
JSONObject response = abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
JSONObject response = abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
verify(postMethod).setURI(new URI("http://new/URL", true));
assertEquals("Unexpected JSON response received.", "{}", response.toString()); verify(postMethod).setURI(new URI("http://new/URL", true));
} assertEquals("Unexpected JSON response received.", "{}", response.toString());
}
@Test
public void testPostQuery_handlesIOException() throws Exception @Test
{ public void testPostQuery_handlesIOException() throws Exception
String messageFromHttp = "Some IO Exception"; {
when(httpClient.executeMethod(any())).thenThrow(new IOException(messageFromHttp)); String messageFromHttp = "Some IO Exception";
when(httpClient.executeMethod(any())).thenThrow(new IOException(messageFromHttp));
HttpClientException expectedException =
assertThrows(HttpClientException.class, () -> abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body)); HttpClientException expectedException = assertThrows(HttpClientException.class, () -> abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body));
String exceptionMessage = expectedException.getMessage(); String exceptionMessage = expectedException.getMessage();
assertTrue(exceptionMessage.endsWith("[%s] %s".formatted(abstractSolrQueryHTTPClient.getClass().getSimpleName(), messageFromHttp))); assertTrue(exceptionMessage.endsWith("[%s] %s".formatted(abstractSolrQueryHTTPClient.getClass().getSimpleName(), messageFromHttp)));
} }
/** Create an input stream containing the given string. */ /** Create an input stream containing the given string. */
private ByteArrayInputStream convertStringToInputStream(String message) private ByteArrayInputStream convertStringToInputStream(String message)
{ {
return new ByteArrayInputStream(message.getBytes()); return new ByteArrayInputStream(message.getBytes());
} }
} }