2017-08-30

Weblogic: JAR entry META-INF/adfm.xml not found in _wl_cls_gen.jar

You've just deployed your favorite EAR in a WebLogic successfully, but when you open it, an unfriendly HTTP 500 greets you. This usually happens in development mode.
Error 500--Internal Server Error

oracle.jbo.JboException: JBO-29114 ADFContext is not setup to process messages for this exception. Use the exception stack trace and error code to investigate the root cause of this exception. Root cause error code is JBO-29000. Error message parameters are {0=java.io.FileNotFoundException, 1=JAR entry META-INF/adfm.xml not found in C:\Users\yolo\AppData\Roaming\JDeveloper\system12.2.1.2.42.170105.1224\DefaultDomain\servers\DefaultServer\tmp\_WL_user\OalCdqWorkbench\dfoxis\war\WEB-INF\lib\_wl_cls_gen.jar}
 at oracle.jbo.uicli.mom.CpxUtils.getCpxListFromMetadata(CpxUtils.java:512)
 at oracle.jbo.uicli.mom.JUMetaObjectManager.loadCpx(JUMetaObjectManager.java:915)
 at oracle.adf.model.BindingContext.initialize(BindingContext.java:469)
 at oracle.adf.model.BindingRequestHandler.beginRequest(BindingRequestHandler.java:270)
 at oracle.adf.model.servlet.ADFBindingFilter.doFilter(ADFBindingFilter.java:196)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at oracle.adfinternal.view.faces.webapp.rich.RegistrationFilter.doFilter(RegistrationFilter.java:105)
 at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:529)
 at oracle.adfinternal.view.faces.activedata.AdsFilter.doFilter(AdsFilter.java:60)
 at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:529)
 at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:354)
 at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:232)
 at org.apache.myfaces.trinidad.webapp.TrinidadFilter.doFilter(TrinidadFilter.java:92)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at oracle.adf.library.webapp.LibraryFilter.doFilter(LibraryFilter.java:169)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at oracle.security.jps.ee.http.JpsAbsFilter$1.run(JpsAbsFilter.java:141)
 at java.security.AccessController.doPrivileged(Native Method)
 at oracle.security.jps.util.JpsSubject.doAsPrivileged(JpsSubject.java:315)
 at oracle.security.jps.ee.util.JpsPlatformUtil.runJaasMode(JpsPlatformUtil.java:650)
 at oracle.security.jps.ee.http.JpsAbsFilter.runJaasMode(JpsAbsFilter.java:124)
 at oracle.security.jps.ee.http.JpsAbsFilter.doFilter(JpsAbsFilter.java:232)
 at oracle.security.jps.ee.http.JpsFilter.doFilter(JpsFilter.java:94)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at oracle.dms.servlet.DMSServletFilter.doFilter(DMSServletFilter.java:248)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:32)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
 at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3683)
 at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3649)
 at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:326)
 at weblogic.security.service.SecurityManager.runAsForUserCode(SecurityManager.java:197)
 at weblogic.servlet.provider.WlsSecurityProvider.runAsForUserCode(WlsSecurityProvider.java:203)
 at weblogic.servlet.provider.WlsSubjectHandle.run(WlsSubjectHandle.java:71)
 at weblogic.servlet.internal.WebAppServletContext.doSecuredExecute(WebAppServletContext.java:2433)
 at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2281)
 at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2259)
 at weblogic.servlet.internal.ServletRequestImpl.runInternal(ServletRequestImpl.java:1691)
 at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1651)
 at weblogic.servlet.provider.ContainerSupportProviderImpl$WlsRequestExecutor.run(ContainerSupportProviderImpl.java:270)
 at weblogic.invocation.ComponentInvocationContextManager._runAs(ComponentInvocationContextManager.java:348)
 at weblogic.invocation.ComponentInvocationContextManager.runAs(ComponentInvocationContextManager.java:333)
 at weblogic.work.LivePartitionUtility.doRunWorkUnderContext(LivePartitionUtility.java:54)
 at weblogic.work.PartitionUtility.runWorkUnderContext(PartitionUtility.java:41)
 at weblogic.work.SelfTuningWorkManagerImpl.runWorkUnderContext(SelfTuningWorkManagerImpl.java:640)
 at weblogic.work.ExecuteThread.execute(ExecuteThread.java:406)
 at weblogic.work.ExecuteThread.run(ExecuteThread.java:346)
Caused by: java.io.FileNotFoundException: JAR entry META-INF/adfm.xml not found in C:\Users\yolo\AppData\Roaming\JDeveloper\system12.2.1.2.42.170105.1224\DefaultDomain\servers\DefaultServer\tmp\_WL_user\OalCdqWorkbench\dfoxis\war\WEB-INF\lib\_wl_cls_gen.jar
 at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:142)
 at sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:150)
 at java.net.URL.openStream(URL.java:1045)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.openStream(MetadataRegistryImpl.java:650)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.getDomDocument(MetadataRegistryImpl.java:665)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.getRegistryPaths(MetadataRegistryImpl.java:247)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.visitRegistryPaths(MetadataRegistryImpl.java:160)
 at oracle.adf.share.common.rc.util.impl.MetadataRegistryImpl.visitRegistryPaths(MetadataRegistryImpl.java:116)
 at oracle.jbo.uicli.mom.CpxUtils.getCpxListFromMetadata(CpxUtils.java:491)
 ... 46 more

What to do: Restart WebLogic.

The issue is handled here: https://support.oracle.com/knowledge/Middleware/2244836_1.html
This is a caching issue, so to permanently fix it, upgrade your JDeveloper.

Javascript: Display Image Fetched Through Authenticated REST API

I have a protected REST endpoint in Oracle Content and Experience Cloud, which returns image/png. Let's display it.

var imgUrl = "https://foo.barcloud.com/documents/api/latest/files/FA5A98CDC6482BD3ED6CFEA98CD980E/data/thumbnail";
var user = "foobar";
var pass = "Yolo6969";

// @see https://stackoverflow.com/a/17682424/357403

var xhr = new XMLHttpRequest();

// Success
xhr.addEventListener("load", function __xhrLoaded() {
    var img = document.getElementById('thumbnail');
    // This will create the DataURI for us.
    var URL = window.URL || window.webkitURL;

    switch (this.responseType) {
    case "arraybuffer":
        // createObjectURL only accepts Blob.
        img.src = URL.createObjectURL(new Blob([ this.response ], { type : this.getResponseHeader('Content-Type') }));
        break;
    case "blob":
        img.src = URL.createObjectURL(this.response);
        break;
    default:
        console.error("Unsupported response type: ", this.responseType, " -- expected binary types for image data.");
        break;
    }
});

// Failure
xhr.addEventListener("error", function __xhrFailed() {
    console.log("Request failed.", this);
});

// open (method, uri, isAsync, user, passwd)
xhr.open('GET', imgUrl, true, user, pass);
// Yes, I want to authenticate.
xhr.withCredentials = true;
// I want to authenticate right now.
xhr.setRequestHeader("Authorization", "Basic " + btoa(user + ':' + pass));
// Give me a Blob in the `response` field.
xhr.responseType = "blob";
xhr.overrideMimeType("image/png");
// Execute the action.
xhr.send();

2017-08-23

Java: CORS Filter for Jersey

So you want to add CORS support for your Jersey server.

Add this filter:

package org.example.rest.service.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

import org.glassfish.jersey.server.ExtendedUriInfo;

@Provider
@Priority(Priorities.HEADER_DECORATOR)
public class CORSFilter implements ContainerRequestFilter, ContainerResponseFilter {
 private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
 private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
 private static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
 private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
 private static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
 private static final String AUTHORIZATION = "authorization";
 private static final String ORIGIN = "Origin";

 private static String extractAllowedMethods(final ExtendedUriInfo extendedUriInfo) {
  final Optional<Class<?>> optional = extendedUriInfo.getMatchedRuntimeResources().stream()
    .flatMap(r -> r.getResources().stream()).flatMap(r -> r.getHandlerClasses().stream())
    .filter(r -> r.getPackage().getName().startsWith("org.example.rest.service")).findFirst();

  if (optional.isPresent()) {
   return Arrays.stream(optional.get().getDeclaredMethods())//
     .flatMap(m -> Arrays.stream(m.getAnnotations()))//
     .map(a -> a.annotationType().getAnnotation(javax.ws.rs.HttpMethod.class))//
     .filter(Objects::nonNull)//
     .map(HttpMethod::value)//
     .distinct()//
     .collect(Collectors.joining(", "));
  }
  // Returning OPTIONS is a bit shady, as ACAM is about *real*, actual methods only.
  return "OPTIONS";
 }

 @Context
 private HttpServletRequest request;

 @Context
 private ExtendedUriInfo extendedUriInfo;

 @Override
 public void filter(final ContainerRequestContext requestContext) throws IOException {
  final String origin = requestContext.getHeaderString(ORIGIN);
  if (origin != null && "OPTIONS".equals(requestContext.getMethod())) {
   request.setAttribute(this.getClass().getName(), true);
   requestContext.abortWith(Response.ok("CORS OK, carry on.", MediaType.TEXT_PLAIN_TYPE).build());
  }
 }

 /**
  * @see https://www.w3.org/TR/cors/
  * @see https://jmchung.github.io/blog/2013/08/11/cross-domain-on-jersey-restful-web-services/
  * @see https://solutionsisee.wordpress.com/2016/06/30/adding-cors-support-in-jersey-server/
  */
 @Override
 public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
   throws IOException {

  final MultivaluedMap<String, Object> responseHeaders = responseContext.getHeaders();
  final String origin = requestContext.getHeaderString(ORIGIN);

  if (origin != null) {
   // The presence of the Origin header marks a CORS request.
   responseHeaders.add(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
   responseHeaders.add(ACCESS_CONTROL_ALLOW_METHODS, extractAllowedMethods(extendedUriInfo));
   responseHeaders.add(ACCESS_CONTROL_ALLOW_HEADERS, AUTHORIZATION + ", X-Requested-With, Content-Type");
   if (requestContext.getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS) != null) {
    responseHeaders.add(ACCESS_CONTROL_ALLOW_CREDENTIALS, requestContext
      .getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS).toLowerCase().contains(AUTHORIZATION));
   }
  }

  if (request.getAttribute(this.getClass().getName()) != null) {
   // We are in a CORS Preflight answer, fast tracked. The entity (== response body) is not
   // relevant.
  }
 }
}

It is almost implementation agnostic, except for the ExtendedUriInfo.