/** * The cause of a ForeignException can be an exception that was generated on a local in process * thread, or a thread from a 'remote' separate process. * * If the cause is a ProxyThrowable, we know it came from deserialization which usually means * it came from not only another thread, but also from a remote thread. * * @return true if went through deserialization, false if locally generated */ public boolean isRemote() { return getCause() instanceof ProxyThrowable; }
@Override public String toString() { String className = getCause().getClass().getName() ; return className + " via " + getSource() + ":" + getLocalizedMessage(); }
@Override public synchronized void rethrowException() throws ForeignException { if (exception != null) { // This gets the stack where this is caused, (instead of where it was deserialized). // This is much more useful for debugging throw new ForeignException(exception.getSource(), exception.getCause()); } }
@Override public void receive(ForeignException ee) { // if this is a notification from a remote source, just log if (ee.isRemote()) { LOG.debug("Was remote foreign exception, not redispatching error", ee); return; } // if this is a local KeeperException, don't attempt to notify other members if (ee.getCause() instanceof KeeperException) { LOG.debug("Was KeeperException, not redispatching error", ee); return; } // if it is other local error, then send it to the coordinator try { rpcs.sendMemberAborted(Subprocedure.this, ee); } catch (IOException e) { // this will fail all the running procedures, since the connection is down LOG.error("Can't reach controller, not propagating error", e); } } });
/** * Compare that a generic exception's stack trace has the same stack trace elements after * serialization and deserialization */ @Test public void testRemoteFromLocal() throws IOException { String errorMsg = "some message"; Exception generic = new Exception(errorMsg); generic.printStackTrace(); assertTrue(generic.getMessage().contains(errorMsg)); ForeignException e = ForeignException.deserialize(ForeignException.serialize(srcName, generic)); assertArrayEquals("Local stack trace got corrupted", generic.getStackTrace(), e.getCause().getStackTrace()); e.printStackTrace(); // should have ForeignException and source node in it. assertTrue(e.getCause().getCause() == null); // verify that original error message is present in Foreign exception message assertTrue(e.getCause().getMessage().contains(errorMsg)); }
/** * Verify that we get back similar stack trace information before an after serialization. */ @Test public void testSimpleException() throws IOException { String data = "some bytes"; ForeignException in = new ForeignException("SRC", new IllegalArgumentException(data)); // check that we get the data back out ForeignException e = ForeignException.deserialize(ForeignException.serialize(srcName, in)); assertNotNull(e); // now check that we get the right stack trace StackTraceElement elem = new StackTraceElement(this.getClass().toString(), "method", "file", 1); in.setStackTrace(new StackTraceElement[] { elem }); e = ForeignException.deserialize(ForeignException.serialize(srcName, in)); assertNotNull(e); assertEquals("Stack trace got corrupted", elem, e.getCause().getStackTrace()[0]); assertEquals("Got an unexpectedly long stack trace", 1, e.getCause().getStackTrace().length); }
/** * Tests that a dispatcher only dispatches only the first exception, and does not propagate * subsequent exceptions. */ @Test public void testErrorPropagation() { ForeignExceptionListener listener1 = Mockito.mock(ForeignExceptionListener.class); ForeignExceptionListener listener2 = Mockito.mock(ForeignExceptionListener.class); ForeignExceptionDispatcher dispatcher = new ForeignExceptionDispatcher(); // add the listeners dispatcher.addListener(listener1); dispatcher.addListener(listener2); // create an artificial error dispatcher.receive(EXTEXN); // make sure the listeners got the error Mockito.verify(listener1, Mockito.times(1)).receive(EXTEXN); Mockito.verify(listener2, Mockito.times(1)).receive(EXTEXN); // make sure that we get an exception try { dispatcher.rethrowException(); fail("Monitor should have thrown an exception after getting error."); } catch (ForeignException ex) { assertTrue("Got an unexpected exception:" + ex, ex.getCause() == EXTEXN.getCause()); LOG.debug("Got the testing exception!"); } // push another error, which should be not be passed to listeners dispatcher.receive(EXTEXN2); Mockito.verify(listener1, Mockito.never()).receive(EXTEXN2); Mockito.verify(listener2, Mockito.never()).receive(EXTEXN2); }
/** * Checks if the specified snapshot is done. * @return true if the snapshot is in file system ready to use, * false if the snapshot is in the process of completing * @throws ServiceException wrapping UnknownSnapshotException if invalid snapshot, or * a wrapped HBaseSnapshotException with progress failure reason. */ @Override public IsSnapshotDoneResponse isSnapshotDone(RpcController controller, IsSnapshotDoneRequest request) throws ServiceException { LOG.debug("Checking to see if snapshot from request:" + ClientSnapshotDescriptionUtils.toString(request.getSnapshot()) + " is done"); try { master.checkInitialized(); IsSnapshotDoneResponse.Builder builder = IsSnapshotDoneResponse.newBuilder(); boolean done = master.snapshotManager.isSnapshotDone(request.getSnapshot()); builder.setDone(done); return builder.build(); } catch (ForeignException e) { throw new ServiceException(e.getCause()); } catch (IOException e) { throw new ServiceException(e); } }
/** * Execute Restore/Clone snapshot operation. * * <p>If the specified table exists a "Restore" is executed, replacing the table * schema and directory data with the content of the snapshot. * The table must be disabled, or a UnsupportedOperationException will be thrown. * * <p>If the table doesn't exist a "Clone" is executed, a new table is created * using the schema at the time of the snapshot, and the content of the snapshot. * * <p>The restore/clone operation does not require copying HFiles. Since HFiles * are immutable the table can point to and use the same files as the original one. */ @Override public RestoreSnapshotResponse restoreSnapshot(RpcController controller, RestoreSnapshotRequest request) throws ServiceException { try { long procId = master.restoreSnapshot(request.getSnapshot(), request.getNonceGroup(), request.getNonce(), request.getRestoreACL()); return RestoreSnapshotResponse.newBuilder().setProcId(procId).build(); } catch (ForeignException e) { throw new ServiceException(e.getCause()); } catch (IOException e) { throw new ServiceException(e); } }
/** * Checks if the specified procedure is done. * @return true if the procedure is done, false if the procedure is in the process of completing * @throws ServiceException if invalid procedure or failed procedure with progress failure reason. */ @Override public IsProcedureDoneResponse isProcedureDone(RpcController controller, IsProcedureDoneRequest request) throws ServiceException { try { master.checkInitialized(); ProcedureDescription desc = request.getProcedure(); MasterProcedureManager mpm = master.getMasterProcedureManagerHost().getProcedureManager( desc.getSignature()); if (mpm == null) { throw new ServiceException("The procedure is not registered: " + desc.getSignature()); } LOG.debug("Checking to see if procedure from request:" + desc.getSignature() + " is done"); IsProcedureDoneResponse.Builder builder = IsProcedureDoneResponse.newBuilder(); boolean done = mpm.isProcedureDone(desc); builder.setDone(done); return builder.build(); } catch (ForeignException e) { throw new ServiceException(e.getCause()); } catch (IOException e) { throw new ServiceException(e); } }
/** * Triggers an asynchronous attempt to run a distributed procedure. * {@inheritDoc} */ @Override public ExecProcedureResponse execProcedure(RpcController controller, ExecProcedureRequest request) throws ServiceException { try { master.checkInitialized(); ProcedureDescription desc = request.getProcedure(); MasterProcedureManager mpm = master.getMasterProcedureManagerHost().getProcedureManager( desc.getSignature()); if (mpm == null) { throw new ServiceException(new DoNotRetryIOException("The procedure is not registered: " + desc.getSignature())); } LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " + desc.getSignature()); mpm.checkPermissions(desc, accessChecker, RpcServer.getRequestUser().orElse(null)); mpm.execProcedure(desc); // send back the max amount of time the client should wait for the procedure // to complete long waitTime = SnapshotDescriptionUtils.DEFAULT_MAX_WAIT_TIME; return ExecProcedureResponse.newBuilder().setExpectedTimeout( waitTime).build(); } catch (ForeignException e) { throw new ServiceException(e.getCause()); } catch (IOException e) { throw new ServiceException(e); } }
/** * Triggers an asynchronous attempt to take a snapshot. * {@inheritDoc} */ @Override public SnapshotResponse snapshot(RpcController controller, SnapshotRequest request) throws ServiceException { try { master.checkInitialized(); master.snapshotManager.checkSnapshotSupport(); LOG.info(master.getClientIdAuditPrefix() + " snapshot request for:" + ClientSnapshotDescriptionUtils.toString(request.getSnapshot())); // get the snapshot information SnapshotDescription snapshot = SnapshotDescriptionUtils.validate( request.getSnapshot(), master.getConfiguration()); master.snapshotManager.takeSnapshot(snapshot); // send back the max amount of time the client should wait for the snapshot to complete long waitTime = SnapshotDescriptionUtils.getMaxMasterTimeout(master.getConfiguration(), snapshot.getType(), SnapshotDescriptionUtils.DEFAULT_MAX_WAIT_TIME); return SnapshotResponse.newBuilder().setExpectedTimeout(waitTime).build(); } catch (ForeignException e) { throw new ServiceException(e.getCause()); } catch (IOException e) { throw new ServiceException(e); } }
/** * The cause of a ForeignException can be an exception that was generated on a local in process * thread, or a thread from a 'remote' separate process. * * If the cause is a ProxyThrowable, we know it came from deserialization which usually means * it came from not only another thread, but also from a remote thread. * * @return true if went through deserialization, false if locally generated */ public boolean isRemote() { return getCause() instanceof ProxyThrowable; }
/** * The cause of a ForeignException can be an exception that was generated on a local in process * thread, or a thread from a 'remote' separate process. * * If the cause is a ProxyThrowable, we know it came from deserialization which usually means * it came from not only another thread, but also from a remote thread. * * @return true if went through deserialization, false if locally generated */ public boolean isRemote() { return getCause() instanceof ProxyThrowable; }
@Override public String toString() { String className = getCause().getClass().getName() ; return className + " via " + getSource() + ":" + getLocalizedMessage(); }
@Override public String toString() { String className = getCause().getClass().getName() ; return className + " via " + getSource() + ":" + getLocalizedMessage(); }
@Override public synchronized void rethrowException() throws ForeignException { if (exception != null) { // This gets the stack where this is caused, (instead of where it was deserialized). // This is much more useful for debugging throw new ForeignException(exception.getSource(), exception.getCause()); } }
@Override public synchronized void rethrowException() throws ForeignException { if (exception != null) { // This gets the stack where this is caused, (instead of where it was deserialized). // This is much more useful for debugging throw new ForeignException(exception.getSource(), exception.getCause()); } }
/** * Compare that a generic exception's stack trace has the same stack trace elements after * serialization and deserialization */ @Test public void testRemoteFromLocal() throws IOException { String errorMsg = "some message"; Exception generic = new Exception(errorMsg); generic.printStackTrace(); assertTrue(generic.getMessage().contains(errorMsg)); ForeignException e = ForeignException.deserialize(ForeignException.serialize(srcName, generic)); assertArrayEquals("Local stack trace got corrupted", generic.getStackTrace(), e.getCause().getStackTrace()); e.printStackTrace(); // should have ForeignException and source node in it. assertTrue(e.getCause().getCause() == null); // verify that original error message is present in Foreign exception message assertTrue(e.getCause().getMessage().contains(errorMsg)); }
/** * Verify that we get back similar stack trace information before an after serialization. */ @Test public void testSimpleException() throws IOException { String data = "some bytes"; ForeignException in = new ForeignException("SRC", new IllegalArgumentException(data)); // check that we get the data back out ForeignException e = ForeignException.deserialize(ForeignException.serialize(srcName, in)); assertNotNull(e); // now check that we get the right stack trace StackTraceElement elem = new StackTraceElement(this.getClass().toString(), "method", "file", 1); in.setStackTrace(new StackTraceElement[] { elem }); e = ForeignException.deserialize(ForeignException.serialize(srcName, in)); assertNotNull(e); assertEquals("Stack trace got corrupted", elem, e.getCause().getStackTrace()[0]); assertEquals("Got an unexpectedly long stack trace", 1, e.getCause().getStackTrace().length); }