private void trace(Type type, String id, ModelNode operation) { if (!trackInvocations) return; if (Type.BEGIN.equals(type)) { diagnostics.logRpc(type.getClassifier(), id, System.currentTimeMillis(), getToken(operation)); } else { diagnostics.logRpc(type.getClassifier(), id, System.currentTimeMillis()); } }
/** * @author Heiko Braun * @date 3/17/11 */ @SuppressWarnings("GwtClientClassFromNonInheritedModule") public class DMRHandler implements ActionHandler<DMRAction, DMRResponse> { private static final String HEADER_CONTENT_TYPE = "Content-Type"; private static final String HEADER_ACCEPT = "Accept"; private static final String DMR_ENCODED = "application/dmr-encoded"; private static final String HEADER_CONNECTION = "Connection"; private static final String KEEP_ALIVE = "Keep-Alive"; /** * The read resource description supports the following parameters: recursive, proxies, * operations, inherited plus one not documented: locale. See * https://docs.jboss.org/author/display/AS72/Global+operations#Globaloperations-readresourcedescription * for a more detailed description */ private static final String[] READ_RESOURCE_DESCRIPTION_OPTIONAL_PARAMETERS = new String[] {RECURSIVE, PROXIES, OPERATIONS, INHERITED, LOCALE}; private static long idCounter = 0; private RequestBuilder prb; private Diagnostics diagnostics = GWT.create(Diagnostics.class); private boolean trackInvocations = diagnostics.isEnabled(); private DMREndpointConfig endpointConfig = GWT.create(DMREndpointConfig.class); private ResourceAccessLog resourceLog = ResourceAccessLog.INSTANCE; private RequestBuilder postRequestBuilder() { // lazy init, because endpointConfig.getUrl() is not initialized at construction time if (prb == null) { prb = new RequestBuilder(RequestBuilder.POST, endpointConfig.getUrl()); prb.setHeader(HEADER_ACCEPT, DMR_ENCODED); prb.setHeader(HEADER_CONTENT_TYPE, DMR_ENCODED); prb.setIncludeCredentials(true); } return prb; } private static native void redirect(String url) /*-{ $wnd.location = url; }-*/; private static String getToken(ModelNode operation) { StringBuffer sb = new StringBuffer(); if (operation.get(OP).asString().equals(COMPOSITE)) { for (ModelNode step : operation.get(STEPS).asList()) { sb.append(" _").append(getOpToken(step)); } } else { sb.append(getOpToken(operation)); } return sb.toString(); } private static String getOpToken(ModelNode operation) { StringBuffer sb = new StringBuffer(); sb.append(operation.get(ADDRESS).asString()) .append(": ") .append(operation.get(OP)) .append("; ") .append(operation.get(CHILD_TYPE).asString()) .append("; "); if (operation.get(NAME).isDefined()) { sb.append(operation.get(NAME).asString()); } return sb.toString(); } @Override public DispatchRequest execute( DMRAction action, final AsyncCallback<DMRResponse> resultCallback, Map<String, String> properties) { assert action.getOperation() != null; final ModelNode operation = action.getOperation(); // diagnostics, development only if (!GWT.isScript()) { Scheduler.get() .scheduleDeferred( new Scheduler.ScheduledCommand() { @Override public void execute() { decomposeAndLog(operation); } }); } // Request request = executeRequest(resultCallback, GWT.isScript() ? operation : // runAsRole(operation)); // TODO: https://issues.jboss.org/browse/HAL-100 Request request = executeRequest(resultCallback, runAsRole(operation, properties)); return new DispatchRequestHandle(request); } private ModelNode runAsRole(final ModelNode operation, final Map<String, String> properties) { String role = properties.get("run_as"); if (role != null && !operation.get(OP).asString().equals("whoami")) // otherwise we get the replacement role { operation.get("operation-headers").get("roles").set(role); } return operation; } private void decomposeAndLog(ModelNode operation) { if (operation.get(OP).asString().equals(COMPOSITE)) { List<ModelNode> steps = operation.get(STEPS).asList(); for (ModelNode step : steps) logAtomicOperation(step); } else { logAtomicOperation(operation); } } private void logAtomicOperation(ModelNode operation) { if (operation.get(OP).asString().equals(COMPOSITE)) // nested composite ops? { Log.error("Failed to to log resources access", operation.toString()); } else if (operation.hasDefined(CHILD_TYPE)) { // ModelNode address = operation.get(ADDRESS).clone(); // address.add(operation.get(CHILD_TYPE).toString(), "*"); resourceLog.log( Window.Location.getHash(), operation.get(ADDRESS).toString() + " : " + operation.get(OP).asString() + "(child-type=" + operation.get(CHILD_TYPE) + ")"); } else { resourceLog.log( Window.Location.getHash(), operation.get(ADDRESS).toString() + " : " + operation.get(OP).asString()); } } @Override public DispatchRequest undo(DMRAction action, DMRResponse result, AsyncCallback<Void> callback) { throw new RuntimeException("Not implemented yet."); } private Request executeRequest( final AsyncCallback<DMRResponse> resultCallback, final ModelNode operation) { if (idCounter == Long.MAX_VALUE) { idCounter = 0; } Request request = null; try { final String id = String.valueOf(idCounter++); trace(Type.BEGIN, id, operation); final RequestBuilder requestBuilder = chooseRequestBuilder(operation); trace(Type.SERIALIZED, id, operation); final RequestCallback requestCallback = new RequestCallback() { @Override public void onResponseReceived(Request request, Response response) { trace(Type.RECEIVE, id, operation); int statusCode = response.getStatusCode(); if (200 == statusCode) { resultCallback.onSuccess( new DMRResponse( requestBuilder.getHTTPMethod(), response.getText(), response.getHeader(HEADER_CONTENT_TYPE))); } else if (401 == statusCode || 0 == statusCode) { resultCallback.onFailure(new DispatchError("Authentication required.", statusCode)); } else if (403 == statusCode) { resultCallback.onFailure(new DispatchError("Authentication required.", statusCode)); } else if (307 == statusCode) { String location = response.getHeader("Location"); Log.error("Redirect '" + location + "'. Could not execute " + operation.toString()); redirect(location); } else if (503 == statusCode) { resultCallback.onFailure( new DispatchError( "Service temporarily unavailable. Is the server still booting?", statusCode)); } else { StringBuilder sb = new StringBuilder(); sb.append("Unexpected HTTP response").append(": ").append(statusCode); sb.append("\n\n"); sb.append("Request\n"); sb.append(operation.toString()); sb.append("\n\nResponse\n\n"); sb.append(response.getStatusText()).append("\n"); String payload = response.getText().equals("") ? "No details" : ModelNode.fromBase64(response.getText()).toString(); sb.append(payload); resultCallback.onFailure(new DispatchError(sb.toString(), statusCode)); } trace(Type.END, id, operation); } @Override public void onError(Request request, Throwable e) { trace(Type.RECEIVE, id, operation); resultCallback.onFailure(e); trace(Type.END, id, operation); } }; requestBuilder.setCallback(requestCallback); request = requestBuilder.send(); trace(Type.SEND, id, operation); } catch (RequestException e) { resultCallback.onFailure(e); } return request; } static final String[] COLLECTION_OPS = {READ_CHILDREN_RESOURCES_OPERATION}; private static boolean expectCollectionResponse(ModelNode operation) { for (String op : COLLECTION_OPS) { if (op.equals(operation.get(OP).asString())) { return true; } } return false; } private RequestBuilder chooseRequestBuilder(final ModelNode operation) { RequestBuilder requestBuilder; final String op = operation.get(OP).asString(); if (READ_RESOURCE_DESCRIPTION_OPERATION.equals(op)) { String endpoint = endpointConfig.getUrl(); if (endpoint.endsWith("/")) { endpoint = endpoint.substring(0, endpoint.length() - 1); } String descriptionUrl = endpoint + descriptionOperationToUrl(operation); requestBuilder = new RequestBuilder( RequestBuilder.GET, com.google.gwt.http.client.URL.encode(descriptionUrl)); requestBuilder.setHeader(HEADER_ACCEPT, DMR_ENCODED); requestBuilder.setHeader(HEADER_CONTENT_TYPE, DMR_ENCODED); requestBuilder.setIncludeCredentials(true); requestBuilder.setRequestData(null); } else { requestBuilder = postRequestBuilder(); requestBuilder.setRequestData(operation.toBase64String()); } return requestBuilder; } private String descriptionOperationToUrl(final ModelNode operation) { StringBuilder url = new StringBuilder(); final List<Property> address = operation.get(ADDRESS).asPropertyList(); for (Property property : address) { url.append("/").append(property.getName()).append("/").append(property.getValue().asString()); } url.append("?operation=").append("resource-description"); for (String parameter : READ_RESOURCE_DESCRIPTION_OPTIONAL_PARAMETERS) { if (operation.has(parameter)) { url.append("&").append(parameter).append("=").append(operation.get(parameter).asString()); } } return url.toString(); } private void trace(Type type, String id, ModelNode operation) { if (!trackInvocations) return; if (Type.BEGIN.equals(type)) { diagnostics.logRpc(type.getClassifier(), id, System.currentTimeMillis(), getToken(operation)); } else { diagnostics.logRpc(type.getClassifier(), id, System.currentTimeMillis()); } } private static enum Type { BEGIN("begin"), END("end"), SEND("requestSent"), RECEIVE("responseReceived"), SERIALIZED("requestSerialized"), DESERIALIZED("responseDeserialized"); private String classifier; private Type(String classifier) { this.classifier = classifier; } public String getClassifier() { return classifier; } } class DispatchRequestHandle implements DispatchRequest { private Request delegate; DispatchRequestHandle(Request delegate) { this.delegate = delegate; } @Override public void cancel() { if (delegate != null) { delegate.cancel(); } } @Override public boolean isPending() { return delegate != null ? delegate.isPending() : false; } } }