diff --git a/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImpl.java b/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImpl.java index dd9302f6b4..e3da5d49c9 100644 --- a/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImpl.java +++ b/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImpl.java @@ -35,6 +35,8 @@ import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.transfer.TransferException; import org.alfresco.service.cmr.transfer.TransferProgress; import org.alfresco.service.cmr.transfer.TransferTarget; +import org.alfresco.util.json.ExceptionJsonSerializer; +import org.alfresco.util.json.JsonSerializer; import org.apache.commons.httpclient.HostConfiguration; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; @@ -54,6 +56,7 @@ import org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; /** @@ -80,6 +83,8 @@ public class HttpClientTransmitterImpl implements TransferTransmitter private Protocol httpProtocol = new Protocol(HTTP_SCHEME_NAME, new DefaultProtocolSocketFactory(), DEFAULT_HTTP_PORT); private Protocol httpsProtocol = new Protocol(HTTPS_SCHEME_NAME, (ProtocolSocketFactory) new SSLProtocolSocketFactory(), DEFAULT_HTTPS_PORT); private Map protocolMap = null; + private HttpMethodFactory httpMethodFactory = null; + private JsonSerializer jsonErrorSerializer; private ContentService contentService; @@ -91,6 +96,8 @@ public class HttpClientTransmitterImpl implements TransferTransmitter httpClient = new HttpClient(); httpClient.setHttpConnectionManager(new MultiThreadedHttpConnectionManager()); + httpMethodFactory = new StandardHttpMethodFactoryImpl(); + jsonErrorSerializer = new ExceptionJsonSerializer(); } public void init() @@ -123,7 +130,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter */ public void verifyTarget(TransferTarget target) throws TransferException { - HttpMethod verifyRequest = new PostMethod(); + HttpMethod verifyRequest = getPostMethod(); try { HostConfiguration hostConfig = getHostConfig(target); @@ -165,8 +172,8 @@ public class HttpClientTransmitterImpl implements TransferTransmitter log.error("Received \"unsuccessful\" response code from target server: " + response); String errorPayload = method.getResponseBodyAsString(); JSONObject errorObj = new JSONObject(errorPayload); - errorId = errorObj.getString("errorId"); - JSONArray errorParamArray = errorObj.getJSONArray("errorParams"); + errorId = errorObj.getString("alfrescoErrorId"); + JSONArray errorParamArray = errorObj.getJSONArray("alfrescoErrorParams"); int length = errorParamArray.length(); errorParams = new String[length]; for (int i = 0; i < length; ++i) @@ -185,7 +192,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter * @param target * @return */ - private HttpState getHttpState(TransferTarget target) + protected HttpState getHttpState(TransferTarget target) { HttpState httpState = new HttpState(); httpState.setCredentials(new AuthScope(target.getEndpointHost(), target.getEndpointPort(), @@ -219,7 +226,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter public Transfer begin(TransferTarget target) throws TransferException { - HttpMethod beginRequest = new PostMethod(); + HttpMethod beginRequest = getPostMethod(); try { HostConfiguration hostConfig = getHostConfig(target); @@ -263,7 +270,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter public void sendManifest(Transfer transfer, File manifest, OutputStream result) throws TransferException { TransferTarget target = transfer.getTransferTarget(); - PostMethod postSnapshotRequest = new PostMethod(); + PostMethod postSnapshotRequest = getPostMethod(); MultipartRequestEntity requestEntity; if(log.isDebugEnabled()) @@ -332,7 +339,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter public void abort(Transfer transfer) throws TransferException { TransferTarget target = transfer.getTransferTarget(); - HttpMethod abortRequest = new PostMethod(); + HttpMethod abortRequest = getPostMethod(); try { HostConfiguration hostConfig = getHostConfig(target); @@ -370,7 +377,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter public void commit(Transfer transfer) throws TransferException { TransferTarget target = transfer.getTransferTarget(); - HttpMethod commitRequest = new PostMethod(); + HttpMethod commitRequest = getPostMethod(); try { HostConfiguration hostConfig = getHostConfig(target); @@ -407,7 +414,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter public void prepare(Transfer transfer) throws TransferException { TransferTarget target = transfer.getTransferTarget(); - HttpMethod prepareRequest = new PostMethod(); + HttpMethod prepareRequest = getPostMethod(); try { HostConfiguration hostConfig = getHostConfig(target); @@ -452,7 +459,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter } TransferTarget target = transfer.getTransferTarget(); - PostMethod postContentRequest = new PostMethod(); + PostMethod postContentRequest = getPostMethod(); try { @@ -517,7 +524,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter public TransferProgress getStatus(Transfer transfer) throws TransferException { TransferTarget target = transfer.getTransferTarget(); - HttpMethod statusRequest = new PostMethod(); + HttpMethod statusRequest = getPostMethod(); try { HostConfiguration hostConfig = getHostConfig(target); @@ -539,11 +546,15 @@ public class HttpClientTransmitterImpl implements TransferTransmitter int currentPosition = statusObj.getInt("currentPosition"); int endPosition = statusObj.getInt("endPosition"); String statusStr= statusObj.getString("status"); - //We're expecting the transfer progress encoded in a JSON object... + + JSONObject errorJSON = statusObj.getJSONObject("error"); + Throwable throwable = rehydrateError(errorJSON); + TransferProgress p = new TransferProgress(); p.setStatus(TransferProgress.Status.valueOf(statusStr)); p.setCurrentPosition(currentPosition); p.setEndPosition(endPosition); + p.setError(throwable); return p; } catch (RuntimeException e) @@ -569,9 +580,7 @@ public class HttpClientTransmitterImpl implements TransferTransmitter public void getTransferReport(Transfer transfer, OutputStream result) { TransferTarget target = transfer.getTransferTarget(); - PostMethod getReportRequest = new PostMethod(); - MultipartRequestEntity requestEntity; - + PostMethod getReportRequest = getPostMethod(); try { HostConfiguration hostConfig = getHostConfig(target); @@ -621,6 +630,24 @@ public class HttpClientTransmitterImpl implements TransferTransmitter getReportRequest.releaseConnection(); } } + + protected PostMethod getPostMethod() + { + return httpMethodFactory.createPostMethod(); + } + + /** + * + * @param errorJSON A JSON object expected to hold the name of the error class ("errorType"), + * the error message ("errorMessage"), and, optionally, the Alfresco message id ("alfrescoErrorId") + * and Alfresco message parameters ("alfrescoErrorParams"). + * @return The rehydrated error object, or null if errorJSON is null. + * @throws JSONException if an error occurs while parsing the supplied JSON object + */ + private Throwable rehydrateError(JSONObject errorJSON) + { + return jsonErrorSerializer.deserialize(errorJSON); + } public void setContentService(ContentService contentService) { @@ -631,4 +658,15 @@ public class HttpClientTransmitterImpl implements TransferTransmitter { return contentService; } -} // end of class + + public void setHttpMethodFactory(HttpMethodFactory httpMethodFactory) + { + this.httpMethodFactory = httpMethodFactory; + } + + public void setJsonErrorSerializer(JsonSerializer jsonErrorSerializer) + { + this.jsonErrorSerializer = jsonErrorSerializer; + } + +} diff --git a/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java b/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java index 5b7724bf99..1c42df586c 100644 --- a/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java +++ b/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java @@ -18,16 +18,21 @@ */ package org.alfresco.repo.transfer; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; +import java.util.Arrays; import junit.framework.TestCase; import org.alfresco.service.cmr.transfer.TransferException; +import org.alfresco.service.cmr.transfer.TransferProgress; +import org.alfresco.service.cmr.transfer.TransferProgress.Status; +import org.alfresco.util.json.ExceptionJsonSerializer; import org.apache.commons.httpclient.ConnectTimeoutException; import org.apache.commons.httpclient.HostConfiguration; import org.apache.commons.httpclient.HttpClient; @@ -36,7 +41,10 @@ import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; +import org.json.JSONObject; import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; /** * Unit test for HttpClientTransmitterImpl @@ -59,6 +67,7 @@ public class HttpClientTransmitterImplTest extends TestCase private HttpClientTransmitterImpl transmitter; private HttpClient mockedHttpClient; private TransferTargetImpl target; + private MockableHttpMethodFactory mockedHttpMethodFactory; /* (non-Javadoc) * @see junit.framework.TestCase#setUp() @@ -70,7 +79,9 @@ public class HttpClientTransmitterImplTest extends TestCase this.transmitter = new HttpClientTransmitterImpl(); this.mockedHttpClient = mock(HttpClient.class); + this.mockedHttpMethodFactory = new MockableHttpMethodFactory(); transmitter.setHttpClient(mockedHttpClient); + transmitter.setHttpMethodFactory(mockedHttpMethodFactory); this.target = new TransferTargetImpl(); target.setEndpointHost(TARGET_HOST); @@ -195,6 +206,38 @@ public class HttpClientTransmitterImplTest extends TestCase } } + public void testGetStatusErrorRehydration() throws Exception + { + final ExceptionJsonSerializer errorSerializer = new ExceptionJsonSerializer(); + final TransferException expectedException = new TransferException("my message id", new Object[] {"param1", "param2"}); + when(mockedHttpClient.executeMethod(any(HostConfiguration.class), any(HttpMethod.class), + any(HttpState.class))).thenReturn(200); + doAnswer(new Answer() { + @Override + public String answer(InvocationOnMock invocation) throws Throwable + { + JSONObject progressObject = new JSONObject(); + progressObject.put("transferId", "mytransferid"); + progressObject.put("status", Status.ERROR); + progressObject.put("currentPosition", 1); + progressObject.put("endPosition", 10); + JSONObject errorObject = errorSerializer.serialize(expectedException); + progressObject.put("error", errorObject); + return progressObject.toString(); + } + }).when(mockedHttpMethodFactory.latestPostMethod).getResponseBodyAsString(); + + Transfer transfer = new Transfer(); + transfer.setTransferId("mytransferid"); + transfer.setTransferTarget(target); + TransferProgress progress = transmitter.getStatus(transfer); + assertTrue(progress.getError() != null); + assertEquals(expectedException.getClass(), progress.getError().getClass()); + TransferException receivedException = (TransferException)progress.getError(); + assertEquals(expectedException.getMsgId(), receivedException.getMsgId()); + assertTrue(Arrays.deepEquals(expectedException.getMsgParams(), receivedException.getMsgParams())); + } + private static class CustomSocketFactory implements SecureProtocolSocketFactory { @@ -236,6 +279,26 @@ public class HttpClientTransmitterImplTest extends TestCase // TODO Auto-generated method stub return null; } + } + + private static class MockableHttpMethodFactory implements HttpMethodFactory + { + private PostMethod latestPostMethod; + public MockableHttpMethodFactory() + { + reset(); + } + + @Override + public PostMethod createPostMethod() + { + return latestPostMethod; + } + + public void reset() + { + latestPostMethod = spy(new PostMethod());; + } } } diff --git a/source/java/org/alfresco/repo/transfer/HttpMethodFactory.java b/source/java/org/alfresco/repo/transfer/HttpMethodFactory.java new file mode 100644 index 0000000000..c575a0c43e --- /dev/null +++ b/source/java/org/alfresco/repo/transfer/HttpMethodFactory.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.transfer; + +import org.apache.commons.httpclient.methods.PostMethod; + +public interface HttpMethodFactory +{ + PostMethod createPostMethod(); +} diff --git a/source/java/org/alfresco/repo/transfer/StandardHttpMethodFactoryImpl.java b/source/java/org/alfresco/repo/transfer/StandardHttpMethodFactoryImpl.java new file mode 100644 index 0000000000..b39e81fd04 --- /dev/null +++ b/source/java/org/alfresco/repo/transfer/StandardHttpMethodFactoryImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.transfer; + +import org.apache.commons.httpclient.methods.PostMethod; + +public class StandardHttpMethodFactoryImpl implements HttpMethodFactory +{ + + @Override + public PostMethod createPostMethod() + { + return new PostMethod(); + } + +} diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceCallbackTest.java b/source/java/org/alfresco/repo/transfer/TransferServiceCallbackTest.java index 96b43b0eef..c7e36c9bab 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceCallbackTest.java +++ b/source/java/org/alfresco/repo/transfer/TransferServiceCallbackTest.java @@ -58,6 +58,7 @@ import org.alfresco.service.cmr.transfer.TransferEventSendingContent; import org.alfresco.service.cmr.transfer.TransferEventSendingSnapshot; import org.alfresco.service.cmr.transfer.TransferEventSuccess; import org.alfresco.service.cmr.transfer.TransferException; +import org.alfresco.service.cmr.transfer.TransferFailureException; import org.alfresco.service.cmr.transfer.TransferProgress; import org.alfresco.service.cmr.transfer.TransferService2; import org.alfresco.service.cmr.transfer.TransferTarget; @@ -293,7 +294,7 @@ public class TransferServiceCallbackTest extends TestCase verifyCallback(expectedEvents); } - public void xtestErrorDuringCommit() + public void testErrorDuringCommit() { Exception error = new TransferException("Commit failed"); @@ -329,103 +330,111 @@ public class TransferServiceCallbackTest extends TestCase TransferDefinition transferDef = new TransferDefinition(); transferDef.setNodes(folder1, file1, file2, file3); - transferService.transfer(TRANSFER_TARGET_NAME, transferDef, mockedCallback); - - List expectedEvents = new ArrayList(); - TransferEventImpl event; - - event = new TransferEventEnterState(); - event.setTransferState(TransferState.START); - expectedEvents.add(event); - - event = new TransferEventBegin(); - event.setTransferState(TransferState.START); - expectedEvents.add(event); - - event = new TransferEventEndState(); - event.setTransferState(TransferState.START); - expectedEvents.add(event); - - event = new TransferEventEnterState(); - event.setTransferState(TransferState.SENDING_SNAPSHOT); - expectedEvents.add(event); - - event = new TransferEventSendingSnapshot(); - event.setTransferState(TransferState.SENDING_SNAPSHOT); - expectedEvents.add(event); - - event = new TransferEventEndState(); - event.setTransferState(TransferState.SENDING_SNAPSHOT); - expectedEvents.add(event); - - event = new TransferEventEnterState(); - event.setTransferState(TransferState.SENDING_CONTENT); - expectedEvents.add(event); - - event = new TransferEventSendingContent(); - event.setTransferState(TransferState.SENDING_CONTENT); - expectedEvents.add(event); - - event = new TransferEventSendingContent(); - event.setTransferState(TransferState.SENDING_CONTENT); - expectedEvents.add(event); - - event = new TransferEventSendingContent(); - event.setTransferState(TransferState.SENDING_CONTENT); - expectedEvents.add(event); - - event = new TransferEventEndState(); - event.setTransferState(TransferState.SENDING_CONTENT); - expectedEvents.add(event); - - event = new TransferEventEnterState(); - event.setTransferState(TransferState.PREPARING); - expectedEvents.add(event); - - event = new TransferEventEndState(); - event.setTransferState(TransferState.PREPARING); - expectedEvents.add(event); - - event = new TransferEventEnterState(); - event.setTransferState(TransferState.COMMITTING); - expectedEvents.add(event); - - event = new TransferEventCommittingStatus(); - event.setTransferState(TransferState.COMMITTING); - expectedEvents.add(event); - - event = new TransferEventCommittingStatus(); - event.setTransferState(TransferState.COMMITTING); - expectedEvents.add(event); - - event = new TransferEventCommittingStatus(); - event.setTransferState(TransferState.COMMITTING); - expectedEvents.add(event); - - event = new TransferEventCommittingStatus(); - event.setTransferState(TransferState.COMMITTING); - expectedEvents.add(event); - - event = new TransferEventEndState(); - event.setTransferState(TransferState.COMMITTING); - expectedEvents.add(event); - - event = new TransferEventEnterState(); - event.setTransferState(TransferState.ERROR); - expectedEvents.add(event); - - event = new TransferEventError(); - event.setTransferState(TransferState.ERROR); - ((TransferEventError)event).setException(error); - expectedEvents.add(event); - - event = new TransferEventReport(); - expectedEvents.add(event); - - event = new TransferEventReport(); - expectedEvents.add(event); - - verifyCallback(expectedEvents); + try + { + transferService.transfer(TRANSFER_TARGET_NAME, transferDef, mockedCallback); + fail(); + } + catch (TransferFailureException ex) + { + List expectedEvents = new ArrayList(); + TransferEventImpl event; + + event = new TransferEventEnterState(); + event.setTransferState(TransferState.START); + expectedEvents.add(event); + + event = new TransferEventBegin(); + event.setTransferState(TransferState.START); + expectedEvents.add(event); + + event = new TransferEventEndState(); + event.setTransferState(TransferState.START); + expectedEvents.add(event); + + event = new TransferEventEnterState(); + event.setTransferState(TransferState.SENDING_SNAPSHOT); + expectedEvents.add(event); + + event = new TransferEventSendingSnapshot(); + event.setTransferState(TransferState.SENDING_SNAPSHOT); + expectedEvents.add(event); + + event = new TransferEventEndState(); + event.setTransferState(TransferState.SENDING_SNAPSHOT); + expectedEvents.add(event); + + event = new TransferEventEnterState(); + event.setTransferState(TransferState.SENDING_CONTENT); + expectedEvents.add(event); + + event = new TransferEventSendingContent(); + event.setTransferState(TransferState.SENDING_CONTENT); + expectedEvents.add(event); + + event = new TransferEventSendingContent(); + event.setTransferState(TransferState.SENDING_CONTENT); + expectedEvents.add(event); + + event = new TransferEventSendingContent(); + event.setTransferState(TransferState.SENDING_CONTENT); + expectedEvents.add(event); + + event = new TransferEventEndState(); + event.setTransferState(TransferState.SENDING_CONTENT); + expectedEvents.add(event); + + event = new TransferEventEnterState(); + event.setTransferState(TransferState.PREPARING); + expectedEvents.add(event); + + event = new TransferEventEndState(); + event.setTransferState(TransferState.PREPARING); + expectedEvents.add(event); + + event = new TransferEventEnterState(); + event.setTransferState(TransferState.COMMITTING); + expectedEvents.add(event); + + event = new TransferEventCommittingStatus(); + event.setTransferState(TransferState.COMMITTING); + expectedEvents.add(event); + + event = new TransferEventCommittingStatus(); + event.setTransferState(TransferState.COMMITTING); + expectedEvents.add(event); + + event = new TransferEventCommittingStatus(); + event.setTransferState(TransferState.COMMITTING); + expectedEvents.add(event); + + event = new TransferEventCommittingStatus(); + event.setTransferState(TransferState.COMMITTING); + expectedEvents.add(event); + + event = new TransferEventReport(); + event.setTransferState(TransferState.COMMITTING); + expectedEvents.add(event); + + event = new TransferEventReport(); + event.setTransferState(TransferState.COMMITTING); + expectedEvents.add(event); + + event = new TransferEventEndState(); + event.setTransferState(TransferState.COMMITTING); + expectedEvents.add(event); + + event = new TransferEventEnterState(); + event.setTransferState(TransferState.ERROR); + expectedEvents.add(event); + + event = new TransferEventError(); + event.setTransferState(TransferState.ERROR); + ((TransferEventError)event).setException(error); + expectedEvents.add(event); + + verifyCallback(expectedEvents); + } } public void testTargetAlreadyLocked() @@ -440,7 +449,7 @@ public class TransferServiceCallbackTest extends TestCase transferService.transfer(TRANSFER_TARGET_NAME, transferDef, mockedCallback); fail("Transfer expected to throw an exception, but it didn't."); } - catch(TransferException ex) + catch(TransferFailureException ex) { List expectedEvents = new ArrayList(); TransferEventImpl event; @@ -467,7 +476,7 @@ public class TransferServiceCallbackTest extends TestCase event = new TransferEventError(); event.setTransferState(TransferState.ERROR); - ((TransferEventError)event).setException(ex); + ((TransferEventError)event).setException((Exception)ex.getCause()); expectedEvents.add(event); verifyCallback(expectedEvents); diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java b/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java index 69925046bb..259faa5f13 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java +++ b/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java @@ -674,7 +674,14 @@ public class TransferServiceImpl2 implements TransferService2 { targetError = new TransferException(MSG_UNKNOWN_TARGET_ERROR); } - failureException = new TransferException(MSG_TARGET_ERROR, new Object[] {targetError.getMessage()}, targetError); + if (Exception.class.isAssignableFrom(targetError.getClass())) + { + failureException = (Exception)targetError; + } + else + { + failureException = new TransferException(MSG_TARGET_ERROR, new Object[] {targetError.getMessage()}, targetError); + } clientState = ClientTransferState.Finished; break; } diff --git a/source/java/org/alfresco/util/json/ExceptionJsonSerializer.java b/source/java/org/alfresco/util/json/ExceptionJsonSerializer.java new file mode 100644 index 0000000000..3f3c429dbf --- /dev/null +++ b/source/java/org/alfresco/util/json/ExceptionJsonSerializer.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.util.json; + +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.transfer.TransferException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class ExceptionJsonSerializer implements JsonSerializer +{ + private final static Log log = LogFactory.getLog(ExceptionJsonSerializer.class); + + @Override + public Throwable deserialize(JSONObject errorJSON) + { + if (errorJSON == null) + { + return null; + } + + Throwable result = null; + Object createdObject = null; + + try + { + //errorType and errorMessage should always be reported + String errorType = errorJSON.getString("errorType"); + String errorMessage = errorJSON.getString("errorMessage"); + + if (errorType == null) + { + errorType = Exception.class.getName(); + } + if (errorMessage == null) + { + errorMessage = ""; + } + //alfrescoErrorId and alfrescoErrorParams will only appear if the + //throwable object was of a subclass of AlfrescoRuntimeException + String errorId = errorJSON.optString("alfrescoMessageId", null); + Object[] errorParams = new Object[0]; + JSONArray errorParamArray = errorJSON.optJSONArray("alfrescoMessageParams"); + if (errorParamArray != null) + { + int length = errorParamArray.length(); + errorParams = new Object[length]; + for (int i = 0; i < length; ++i) + { + errorParams[i] = errorParamArray.getString(i); + } + } + Class errorClass; + try + { + errorClass = Class.forName(errorType); + } + catch (ClassNotFoundException e) + { + errorClass = Exception.class; + } + Constructor constructor = null; + try + { + try + { + constructor = errorClass.getConstructor(String.class, Object[].class); + createdObject = constructor.newInstance(errorId, errorParams); + } + catch (NoSuchMethodException e) + { + try + { + constructor = errorClass.getConstructor(String.class); + createdObject = constructor.newInstance(errorId == null ? errorMessage : errorId); + } + catch (NoSuchMethodException e1) + { + try + { + constructor = errorClass.getConstructor(); + createdObject = constructor.newInstance(); + } + catch (NoSuchMethodException e2) + { + } + } + } + } + catch(Exception ex) + { + //We don't need to do anything here. Code below will fix things up + } + if (createdObject == null || !Throwable.class.isAssignableFrom(createdObject.getClass())) + { + result = new TransferException(errorId == null ? errorMessage : errorId, errorParams); + } + else + { + result = (Throwable)createdObject; + } + } + catch(JSONException ex) + { + if (log.isDebugEnabled()) + { + log.debug("Failed to deserialize Throwable object from JSON object", ex); + } + } + return result; + } + + @Override + public JSONObject serialize(Throwable object) + { + JSONObject errorObject = new JSONObject(); + + try + { + errorObject.put("errorType", object.getClass().getName()); + errorObject.put("errorMessage", object.getMessage()); + if (AlfrescoRuntimeException.class.isAssignableFrom(object.getClass())) + { + AlfrescoRuntimeException alfEx = (AlfrescoRuntimeException)object; + errorObject.put("alfrescoMessageId", alfEx.getMsgId()); + Object[] msgParams = alfEx.getMsgParams(); + List params = msgParams == null ? Collections.emptyList() : Arrays.asList(msgParams); + errorObject.put("alfrescoMessageParams", params); + } + } + catch (JSONException e) + { + if (log.isDebugEnabled()) + { + log.debug("Failed to serialize Throwable object into JSON object", e); + } + } + return errorObject; + } +} diff --git a/source/java/org/alfresco/util/json/ExceptionJsonSerializerTest.java b/source/java/org/alfresco/util/json/ExceptionJsonSerializerTest.java new file mode 100644 index 0000000000..14935831ce --- /dev/null +++ b/source/java/org/alfresco/util/json/ExceptionJsonSerializerTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.util.json; + +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.json.JSONObject; + + +public class ExceptionJsonSerializerTest extends TestCase +{ + + private ExceptionJsonSerializer serializer; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + serializer = new ExceptionJsonSerializer(); + } + + public void testIllegalArgumentException() + { + Exception expectedException = new IllegalArgumentException("This is the message"); + JSONObject obj = serializer.serialize(expectedException); + Throwable actualException = serializer.deserialize(obj); + assertEquals(expectedException.getClass(), actualException.getClass()); + assertEquals(expectedException.getMessage(), actualException.getMessage()); + } + + public void testAlfrescoRuntimeExceptionWithNoParams() + { + AlfrescoRuntimeException expectedException = new AlfrescoRuntimeException("message id"); + JSONObject obj = serializer.serialize(expectedException); + Throwable actualException = serializer.deserialize(obj); + assertEquals(expectedException.getClass(), actualException.getClass()); + assertEquals(expectedException.getMsgId(), ((AlfrescoRuntimeException)actualException).getMsgId()); + assertTrue(((AlfrescoRuntimeException)actualException).getMsgParams().length == 0); + } + + public void testAlfrescoRuntimeExceptionWithParams() + { + AlfrescoRuntimeException expectedException = new AlfrescoRuntimeException("message id", + new Object[]{"one","two","three"}); + JSONObject obj = serializer.serialize(expectedException); + Throwable actualException = serializer.deserialize(obj); + assertEquals(expectedException.getClass(), actualException.getClass()); + assertEquals(expectedException.getMsgId(), ((AlfrescoRuntimeException)actualException).getMsgId()); + assertTrue(Arrays.deepEquals(expectedException.getMsgParams(), + ((AlfrescoRuntimeException)actualException).getMsgParams())); + } + + public void testAccessDeniedException() + { + AccessDeniedException expectedException = new AccessDeniedException("message id"); + JSONObject obj = serializer.serialize(expectedException); + Throwable actualException = serializer.deserialize(obj); + assertEquals(expectedException.getClass(), actualException.getClass()); + assertEquals(expectedException.getMsgId(), ((AlfrescoRuntimeException)actualException).getMsgId()); + assertTrue(expectedException.getMsgParams() == null); + } +}