/** * Verify that injecting non existing exceptions is flagged with an exception. * * @throws Exception */ public void testInjectingNonExistingComponent() throws Exception { Map<String, Object> attributes = Maps.newHashMap(); Appendable out = new StringBuffer(); Integration integration = service.createIntegration("", Mode.UTEST, true, null); try { integration.injectComponent("foo:bared", attributes, "", "", out); fail( "Instantiating component through integration service should have failed because of missing component def."); } catch (DefinitionNotFoundException expected) { // Expected exception assertTrue(expected.getMessage().contains("No COMPONENT named markup://foo:bared found")); } }
/** Verify that only component defs can be injected. */ public void testInjectingApplications() throws Exception { String validApp = "test:laxSecurity"; Map<String, Object> attributes = Maps.newHashMap(); Appendable out = new StringBuffer(); Integration integration = service.createIntegration("", Mode.UTEST, true, null); try { integration.injectComponent(validApp, attributes, "", "", out); fail("Injecting an application through integration service should have failed."); } catch (DefinitionNotFoundException expected) { // TODO: Maybe a better error message? assertTrue( expected.getMessage().contains("No COMPONENT named markup://test:laxSecurity found")); } }
/** * A private helper routine to make the compiler code more sane. * * <p>This processes a single definition in a dependency tree. It works as a single step in a * breadth first traversal of the tree, accumulating children in the 'deps' set, and updating the * compile context with the current definition. * * <p>Note that once the definition has been retrieved, this code uses the 'canonical' descriptor * from the definition, discarding the incoming descriptor. * * @param descriptor the descriptor that we are currently handling, must not be in the compiling * defs. * @param cc the compile context to allow us to accumulate information. * @param deps the set of dependencies that we are accumulating. * @throws QuickFixException if the definition is not found, or validateDefinition() throws one. */ private <D extends Definition> D getHelper( DefDescriptor<D> descriptor, CompileContext cc, Set<DefDescriptor<?>> deps) throws QuickFixException { CompilingDef<D> cd = cc.getCompiling(descriptor); if (cd.def != null) { return cd.def; } try { if (!fillCompilingDef(cd, cc.context)) { // // At this point, we have failed to get the def, so we should throw an // error. The first stanza is to provide a more useful error description // including the set of components using the missing component. // if (!cd.parents.isEmpty()) { StringBuilder sb = new StringBuilder(); Location handy = null; for (Definition parent : cd.parents) { handy = parent.getLocation(); if (sb.length() != 0) { sb.append(", "); } sb.append(parent.getDescriptor().toString()); } throw new DefinitionNotFoundException(descriptor, handy, sb.toString()); } throw new DefinitionNotFoundException(descriptor); } // // Ok. We have a def. let's figure out what to do with it. // Set<DefDescriptor<?>> newDeps = Sets.newHashSet(); cd.def.appendDependencies(newDeps); // // FIXME: this code will go away with preloads. // This pulls in the context preloads. not pretty, but it works. // if (!cc.addedPreloads && cd.descriptor.getDefType().equals(DefType.APPLICATION)) { cc.addedPreloads = true; Set<String> preloads = cc.context.getPreloads(); for (String preload : preloads) { if (!preload.contains("_")) { DependencyDefImpl.Builder ddb = new DependencyDefImpl.Builder(); ddb.setResource(preload); ddb.setType("APPLICATION,COMPONENT,STYLE,EVENT"); ddb.build().appendDependencies(newDeps); } } } for (DefDescriptor<?> dep : newDeps) { if (!defs.containsKey(dep)) { CompilingDef<?> depcd = cc.getCompiling(dep); depcd.parents.add(cd.def); } } deps.addAll(newDeps); cc.dependencies.put(cd.descriptor, cd.def); return cd.def; } catch (DefinitionNotFoundException dnfe) { // // In the case that we have a DefinitionNotFoundException for our current descriptor, // cache the fact that we didn't find one. // if (dnfe.getDescriptor().equals(descriptor)) { cd.def = null; defs.put(descriptor, cd.def); if (cd.cacheable) { defsCache.put(descriptor, Optional.fromNullable(cd.def)); } } throw dnfe; } }
/** * Handle an exception in the servlet. * * <p>This routine should be called whenever an exception has surfaced to the top level of the * servlet. It should not be overridden unless Aura is entirely subsumed. Most special cases can * be handled by the Aura user by implementing {@link ExceptionAdapter ExceptionAdapter}. * * @param t the throwable to write out. * @param quickfix is this exception a valid quick-fix * @param context the aura context. * @param request the request. * @param response the response. * @param written true if we have started writing to the output stream. * @throws IOException if the output stream does. * @throws ServletException if send404 does (should not generally happen). */ @Override public void handleServletException( Throwable t, boolean quickfix, AuraContext context, HttpServletRequest request, HttpServletResponse response, boolean written) throws IOException { try { Throwable mappedEx = t; boolean map = !quickfix; Format format = context.getFormat(); // // This seems to fail, though the documentation implies that you can do // it. // // if (written && !response.isCommitted()) { // response.resetBuffer(); // written = false; // } if (!written) { // Should we only delete for JSON? setNoCache(response); } if (mappedEx instanceof IOException) { // // Just re-throw IOExceptions. // throw (IOException) mappedEx; } else if (mappedEx instanceof NoAccessException) { Throwable cause = mappedEx.getCause(); String denyMessage = mappedEx.getMessage(); map = false; if (cause != null) { // // Note that the exception handler can remap the cause here. // cause = exceptionAdapter.handleException(cause); denyMessage += ": cause = " + cause.getMessage(); } // // Is this correct?!?!?! // if (format != Format.JSON) { this.send404(request.getServletContext(), request, response); if (!isProductionMode(context.getMode())) { // Preserve new lines and tabs in the stacktrace since this is directly being written on // to the // page denyMessage = "<pre>" + AuraTextUtil.escapeForHTML(denyMessage) + "</pre>"; response.getWriter().println(denyMessage); } return; } } else if (mappedEx instanceof QuickFixException) { if (isProductionMode(context.getMode())) { // // In production environments, we want wrap the quick-fix. But be a little careful here. // We should never mark the top level as a quick-fix, because that means that we gack // on every mis-spelled app. In this case we simply send a 404 and bolt. // if (mappedEx instanceof DefinitionNotFoundException) { DefinitionNotFoundException dnfe = (DefinitionNotFoundException) mappedEx; if (dnfe.getDescriptor() != null && dnfe.getDescriptor().equals(context.getApplicationDescriptor())) { // We're in production and tried to hit an aura app that doesn't exist. // just show the standard 404 page. this.send404(request.getServletContext(), request, response); return; } } map = true; mappedEx = new AuraUnhandledException("404 Not Found (Application Error)", mappedEx); } } if (map) { mappedEx = exceptionAdapter.handleException(mappedEx); } PrintWriter out = response.getWriter(); // // If we have written out data, We are kinda toast in this case. // We really want to roll it all back, but we can't, so we opt // for the best we can do. For HTML we can do nothing at all. // if (format == Format.JSON) { if (!written) { out.write(CSRF_PROTECT); } // // If an exception happened while we were emitting JSON, we want the // client to ignore the now-corrupt data structure. 404s and 500s // cause the client to prepend /*, so we can effectively erase the // bad data by appending a */ here and then serializing the exception // info. // out.write("*/"); // // Unfortunately we can't do the following now. It might be possible // in some cases, but we don't want to go there unless we have to. // } if (format == Format.JS || format == Format.CSS) { // Make sure js and css doesn't get cached in browser, appcache, etc response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); } if (format == Format.JSON || format == Format.HTML || format == Format.JS || format == Format.CSS) { // // We only write out exceptions for HTML or JSON. // Seems bogus, but here it is. // // Start out by cleaning out some settings to ensure we don't // check too many things, leading to a circular failure. Note // that this is still a bit dangerous, as we seem to have a lot // of magic in the serializer. // // Clear the InstanceStack before trying to serialize the exception since the Throwable has // likely // rendered the stack inaccurate, and may falsely trigger NoAccessExceptions. InstanceStack stack = this.contextService.getCurrentContext().getInstanceStack(); List<String> list = stack.getStackInfo(); for (int count = list.size(); count > 0; count--) { stack.popInstance(stack.peek()); } serializationService.write(mappedEx, null, out); if (format == Format.JSON) { out.write("/*ERROR*/"); } } } catch (IOException ioe) { throw ioe; } catch (Throwable death) { // // Catch any other exception and log it. This is actually kinda bad, because something has // gone horribly wrong. We should write out some sort of generic page other than a 404, // but at this point, it is unclear what we can do, as stuff is breaking right and left. // try { response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); exceptionAdapter.handleException(death); if (!isProductionMode(context.getMode())) { response.getWriter().println(death.getMessage()); } } catch (IOException ioe) { throw ioe; } catch (Throwable doubleDeath) { // we are totally hosed. if (!isProductionMode(context.getMode())) { response.getWriter().println(doubleDeath.getMessage()); } } } finally { this.contextService.endContext(); } }