I am trying to get a standalone Jetty server running with a web client. However I am running into issues with the preflight requests that are being sent before the actual request. When I make a request using Postman, I get the expected response (preflight request are not used in Postman), but when the request is made from Chrome I get the following error message:
"XMLHttpRequest cannot load http://my.server.ip:8000/hello/api/test. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access."
The header I get back is as follows:
"HTTP/1.1 200 OK Date: Wed, 26 Oct 2016 14:51:06 GMT Allow: POST,OPTIONS Date: Wed, 26 Oct 2016 14:51:06 GMT Content-Length: 0 Server: Jetty(9.2.10.v20150310)"
My relevant code is shown below. Any help solving this would be greatly appreciated as I have already spent several days trying to figure this out. If there is a way to fix this while still using JAXRSServerFactoryBean that would be great.
Thanks.
My Standalone server class is below:
package com.my.path;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import javax.jws.WebService;
import javax.servlet.DispatcherType;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import org.apache.cxf.binding.BindingFactoryManager;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSBindingFactory;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.codehaus.jettison.mapped.MappedXMLInputFactory;
import org.codehaus.jettison.mapped.MappedXMLOutputFactory;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlets.CrossOriginFilter;
public class myStandaloneServer {
private static Server server = null;
private static HelloWorldImpl module = null;
.
.
.
private static void publishService() throws FaultException {
try {
// Build the module for use in the web service
module = new HelloWorldImpl();
HelloWorld implementor = (HelloWorld) module;
System.out.println("Starting Server");
String address = "http://10.1.10.2:8000/hello";
JAXRSServerFactoryBean svrFactory = new JAXRSServerFactoryBean();
svrFactory.setAddress(address);
svrFactory.setServiceBean(implementor);
svrFactory.setBindingId(JAXRSBindingFactory.JAXRS_BINDING_ID);
BindingFactoryManager manager = svrFactory.getBus().getExtension(BindingFactoryManager.class);
JAXRSBindingFactory factory = new JAXRSBindingFactory();
manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory);
factory.setBus(svrFactory.getBus());
Map<String, Object> properties = new HashMap<String, Object>();
// Don't prefix namespaces...
HashMap<String, String> nstojns = new HashMap<String, String>();
nstojns.put("http://mywebsite.com/", "si");
nstojns.put("http://schemas.xmlsoap.org/soap/envelope/", "");
MappedXMLInputFactory xif = new MappedXMLInputFactory(nstojns);
properties.put(XMLInputFactory.class.getName(), xif);
MappedXMLOutputFactory xof = new MappedXMLOutputFactory(nstojns);
properties.put(XMLOutputFactory.class.getName(), xof);
properties.put("Content-Type", "application/json");
properties.put("Access-Control-Allow-Origin", "*");
properties.put("Access-Control-Allow-Headers", "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers");
svrFactory.setProperties(properties);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/hello");
context.setResourceBase(".");
FilterHolder holder = new FilterHolder (new CrossOriginFilter());
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,content-type,access-control-allow-origin,access-control-allow-methods,access-control-allow-headers");
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
holder.setName("cross-origin");
//FilterMapping fm = new FilterMapping();
//fm.setFilterName("cross-origin");
//fm.setPathSpec("*");
//context.addFilter(holder, fm );
context.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
// svrFactory.setHandler(handlers);
server = svrFactory.create();
} catch (Exception e) {
e.printStackTrace();
logError("Error" + e);
getServerLogger().printStackTrace(e);
}
}
My webs services interface is below:
package com.my.path;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlSeeAlso;
import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;
import org.apache.cxf.rs.security.cors.LocalPreflight;
import com.my.otherPath.data.FaultException;
@CrossOriginResourceSharing(
allowOrigins = {
"*"
},
allowCredentials = true,
maxAge = 1,
allowHeaders = {
"X-Requested-With", "content-type", "access-control-allow-origin", "access-control-allow-methods", "access-control-allow-headers"
}
)
@Path("/api/")
@WebService
@XmlSeeAlso({ com.my.otherPath.data.DateRangeSearchParamItem.class })
public interface HelloWorld {
@GET
@POST
@Produces("text/xml")
@Path("/test")
@CrossOriginResourceSharing(allowAllOrigins = true, allowOrigins = "*", allowHeaders = "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers")
@WebMethod(operationName = "sayHi", exclude = false)
Response sayHi(@FormParam("name") String name);
@OPTIONS
@CrossOriginResourceSharing(allowAllOrigins = true, allowOrigins = "*", allowHeaders = "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers")
@Path("/*")
@LocalPreflight
@WebMethod(operationName = "options", exclude = false)
public abstract Response options() throws FaultException;
}
Here is my implementation:
package com.my.path;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;
import org.apache.cxf.rs.security.cors.LocalPreflight;
@Path("/api/")
@WebService(endpointInterface = "com.my.path.HelloWorld", serviceName = "HelloWorld")
public class HelloWorldImpl implements HelloWorld {
@POST
@Produces("text/xml")
@Path("/test/")
@CrossOriginResourceSharing(allowAllOrigins = true, allowOrigins = "*", allowHeaders = "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers")
@WebMethod(operationName = "sayhHi", exclude = false)
public Response sayHi(@WebParam(targetNamespace = "http://com.mySite/", name="name") String name) {
System.out.println("sayHi called");
return Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
.allow("OPTIONS")
.entity("Hello " + name)
.build();
}
@OPTIONS
@CrossOriginResourceSharing(allowAllOrigins = true, allowOrigins = "*", allowHeaders = "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers")
@Path("/")
@LocalPreflight
@WebMethod(operationName = "options", exclude = false)
public Response options() {
System.out.println("prflight endpoint");
return Response.ok("")
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
.header("Access-Control-Max-Age", "1209600")
.allow("OPTIONS")
.build();
}
}
And here is my web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>CSA</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<description>Apache CXF Endpoint</description>
<display-name>CXF Servlet</display-name>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<!-- <servlet>
<description>JAX-RS Tools Generated - Do not modify</description>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<servlet-class>javax.ws.rs.core.Application</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/jaxrs/*</url-pattern>
</servlet-mapping> -->
<!-- Common JAX-RS Servlet Definition -->
<!-- <servlet>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<servlet-class>javax.ws.rs.core.Application</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping> -->
<!-- This filter is needed for the Access-Control-Allow-Origin header to work. -->
<filter>
<filter-name>cross-origin</filter-name>
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
<init-param>
<param-name>allowedOrigins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>allowedMethods</param-name>
<param-value>GET,POST,OPTIONS,DELETE,PUT</param-value>
</init-param>
<init-param>
<param-name>allowedHeaders</param-name>
<param-value>origin, content-type, accept, authorization, X-Requested-With, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>cross-origin</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>
<!-- This filter is needed for WS clients that can't handle status code 500. It changes the status code to 200. -->
<filter>
<filter-name>ExceptionFilter</filter-name>
<filter-class>csa.core.ws.ExceptionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ExceptionFilter</filter-name>
<servlet-name>cxf</servlet-name>
</filter-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>