Embedded Jetty Error The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved - maven

I'm using Embedded Jetty server to run my tests and Maven for build.
Following is the code used to start Jetty before tests.
System.out.println("Initializing Jetty Server...");
jettyServer = new Server(0);
WebAppContext webapp = new WebAppContext("src/main/webapp", "/testApp");
jettyServer.addHandler(webapp);
jettyServer.start();
int actualPort = jettyServer.getConnectors()[0].getLocalPort();
String baseUrl = "http://localhost:" + actualPort + "/testApp";
All the tests passes if I run it with 'Run as Junit Test'. No problems here.
But if I run it as Maven Test or Maven Install The tests fails withe following cause
Caused by: com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException: 500 /WEB-INF/pages/test1.jsp(3,62) PWC6188: The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application
I have the JSTL dependancy added in POM.xml. Please help to resolve this..

You didn't initialize and setup the environment in a way that is suitable for JSP use.
It requires a bunch of extra work.
You'll need to manipulate classloaders, setup some initializers, declare the javac implementation behavior, and even declare the jsp servlet handling. (missing any one of these and you'll be subject to the environment that you executed under, which is different in your 3 examples)
For a complete example maven project see https://github.com/jetty-project/embedded-jetty-jsp
public class Main
{
// Resource path pointing to where the WEBROOT is
private static final String WEBROOT_INDEX = "/webroot/";
public static void main(String[] args) throws Exception
{
int port = 8080;
LoggingUtil.config();
Log.setLog(new JavaUtilLog());
Main main = new Main(port);
main.start();
main.waitForInterrupt();
}
private static final Logger LOG = Logger.getLogger(Main.class.getName());
private int port;
private Server server;
private URI serverURI;
public Main(int port)
{
this.port = port;
}
public URI getServerURI()
{
return serverURI;
}
public void start() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(port);
server.addConnector(connector);
URL indexUri = this.getClass().getResource(WEBROOT_INDEX);
if (indexUri == null)
{
throw new FileNotFoundException("Unable to find resource " + WEBROOT_INDEX);
}
// Points to wherever /webroot/ (the resource) is
URI baseUri = indexUri.toURI();
// Establish Scratch directory for the servlet context (used by JSP compilation)
File tempDir = new File(System.getProperty("java.io.tmpdir"));
File scratchDir = new File(tempDir.toString(),"embedded-jetty-jsp");
if (!scratchDir.exists())
{
if (!scratchDir.mkdirs())
{
throw new IOException("Unable to create scratch directory: " + scratchDir);
}
}
// Set JSP to use Standard JavaC always
System.setProperty("org.apache.jasper.compiler.disablejsr199","false");
// Setup the basic application "context" for this application at "/"
// This is also known as the handler tree (in jetty speak)
WebAppContext context = new WebAppContext();
context.setContextPath("/");
context.setAttribute("javax.servlet.context.tempdir",scratchDir);
context.setResourceBase(baseUri.toASCIIString());
context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager());
server.setHandler(context);
// Add Application Servlets
context.addServlet(DateServlet.class,"/date/");
//Ensure the jsp engine is initialized correctly
JettyJasperInitializer sci = new JettyJasperInitializer();
ServletContainerInitializersStarter sciStarter = new ServletContainerInitializersStarter(context);
ContainerInitializer initializer = new ContainerInitializer(sci, null);
List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
initializers.add(initializer);
context.setAttribute("org.eclipse.jetty.containerInitializers", initializers);
context.addBean(sciStarter, true);
// Set Classloader of Context to be sane (needed for JSTL)
// JSP requires a non-System classloader, this simply wraps the
// embedded System classloader in a way that makes it suitable
// for JSP to use
ClassLoader jspClassLoader = new URLClassLoader(new URL[0], this.getClass().getClassLoader());
context.setClassLoader(jspClassLoader);
// Add JSP Servlet (must be named "jsp")
ServletHolder holderJsp = new ServletHolder("jsp",JspServlet.class);
holderJsp.setInitOrder(0);
holderJsp.setInitParameter("logVerbosityLevel","DEBUG");
holderJsp.setInitParameter("fork","false");
holderJsp.setInitParameter("xpoweredBy","false");
holderJsp.setInitParameter("compilerTargetVM","1.7");
holderJsp.setInitParameter("compilerSourceVM","1.7");
holderJsp.setInitParameter("keepgenerated","true");
context.addServlet(holderJsp,"*.jsp");
//context.addServlet(holderJsp,"*.jspf");
//context.addServlet(holderJsp,"*.jspx");
// Add Example of mapping jsp to path spec
ServletHolder holderAltMapping = new ServletHolder("foo.jsp", JspServlet.class);
holderAltMapping.setForcedPath("/test/foo/foo.jsp");
context.addServlet(holderAltMapping,"/test/foo/");
// Add Default Servlet (must be named "default")
ServletHolder holderDefault = new ServletHolder("default",DefaultServlet.class);
LOG.info("Base URI: " + baseUri);
holderDefault.setInitParameter("resourceBase",baseUri.toASCIIString());
holderDefault.setInitParameter("dirAllowed","true");
context.addServlet(holderDefault,"/");
// Start Server
server.start();
// Show server state
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(server.dump());
}
// Establish the Server URI
String scheme = "http";
for (ConnectionFactory connectFactory : connector.getConnectionFactories())
{
if (connectFactory.getProtocol().equals("SSL-http"))
{
scheme = "https";
}
}
String host = connector.getHost();
if (host == null)
{
host = "localhost";
}
int port = connector.getLocalPort();
serverURI = new URI(String.format("%s://%s:%d/",scheme,host,port));
LOG.info("Server URI: " + serverURI);
}
public void stop() throws Exception
{
server.stop();
}
/**
* Cause server to keep running until it receives a Interrupt.
* <p>
* Interrupt Signal, or SIGINT (Unix Signal), is typically seen as a result of a kill -TERM {pid} or Ctrl+C
*/
public void waitForInterrupt() throws InterruptedException
{
server.join();
}
}

In the following repo: https://github.com/ericminio/learning-jetty
You can find:
The JstlTest that demoes Jetty serving jsp containing a c:forEach tag
The pom needed to remove the error message about resolving http://java.sun.com/jsp/jstl/core
Hope it helps

Related

Cannot start bundle programmatically

Note: In my case, I'm using Apache Felix implementation if that's matters.
I have written bundle which I'm using as test. It's very simple "Hello World" bundle that do nothing more than print message to stdout when started/stopped:
public class Activator implements BundleActivator {
#Override
public void start(BundleContext context) throws Exception {
System.out.println("Hello, World.");
}
#Override
public void stop(BundleContext context) throws Exception {
System.out.println("Goodbye, World.");
}
}
There is also MANIFEST file which rather pointless to post since when I deploy above bundle through Apache Felix console from standard distribution (which can be downloaded here) bundle starts and print out message.
Next step I'm trying to do is deploy the very same bundle using programmatic approach. Unfortunately this is not working for me. My code looks as follow:
public static void main(String[] args) throws Exception {
FrameworkFactory frameworkFactory = getFrameworkFactory();
Framework framework = frameworkFactory.newFramework(null);
System.out.println("BundleID = " + framework.getBundleId());
System.out.println("State = " + getState(framework.getState()));
framework.init();
System.out.println("BundleID = " + framework.getBundleId());
System.out.println("State = " + getState(framework.getState()));
BundleContext bundleContext = framework.getBundleContext();
bundleContext.addBundleListener((event) -> {
System.out.println("Bundle Changed Event");
});
bundleContext.addFrameworkListener((event) -> {
System.out.println("Framework Event");
});
bundleContext.addServiceListener((event) -> {
System.out.println("Service Changed Event");
});
Bundle bundle = bundleContext.installBundle("file://<absolute-path-to-bundle-jar-same-as-above");
System.out.println("BundleID = " + bundle.getBundleId());
System.out.println("State = " + getState(bundle.getState()));
bundle.start();
System.out.println("BundleID = " + bundle.getBundleId());
System.out.println("State = " + getState(bundle.getState()));
}
private static FrameworkFactory getFrameworkFactory() throws IllegalStateException {
ServiceLoader<FrameworkFactory> loader = ServiceLoader.load(FrameworkFactory.class);
FrameworkFactory factory = null;
for (FrameworkFactory iterator : loader) {
if (factory != null) {
throw new IllegalStateException("Ambiguous SPI implementations.");
}
factory = iterator;
}
return factory;
}
private static String getState(int state) {
switch (state) {
case Bundle.UNINSTALLED:
return "UNINSTALLED";
case Bundle.INSTALLED:
return "INSTALLED";
case Bundle.RESOLVED:
return "RESOLVED";
case Bundle.STARTING:
return "STARTING";
case Bundle.STOPPING:
return "STOPPING";
case Bundle.ACTIVE:
return "ACTIVE";
default:
throw new IllegalStateException("Unknown state");
}
}
The output looks like follow:
BundleID = 0
State = INSTALLED
BundleID = 0
State = STARTING
Bundle Changed Event
BundleID = 1
State = INSTALLED
BundleID = 1
State = INSTALLED
So as far as I understand bundle got installed but last 4 lines indicate that bundle.start() got ignored for some reason.
Could you point out me what am I missing to make this work?
After hour of debugging and reading through javadoc more carefully this is happening because framework was only initialized instead of being started. To make example work you have to simply add framework.start() after framework.init() (or just call framwork.start() which calls framework.init() if found it necessary).
I'm leaving this information as there are few confusing things:
Official documentation to Apache Felix have information about embedding framework into host application. Unfortunately there is only example that use Apache Felix custom mechanisms that make it not portable to other implementations. What is confusing is warning note which if you want to create portable solution you should use init() and getBundleContext(). Whole note cited bellow:
WARNING The felix.systembundle.activators configuration property is specific to the Felix framework implementation. If you want your code to work with other framework implementations, you should call init() on the framework instance and use getBundleContext() directly. Otherwise, the approach would be very similar.
JavaDoc for parameterless version of init() method do not mention about initialization is not same as starting the framework, although init(FrameworkListener...) have such information.
This Framework will not actually be started until start is called.

Spring boot 1.3.x MultipartFile.transferTo null after migration from 1.2.x

I am facing error MultipartFile error for one day after having upgrade from Spring Boot 1.2.7 to 1.3.1.
What I notice is that the default is now Jetty 9.2 and no more Tomcat 8. Everything was fine until I tried to write an uploaded file using MultipartFile.transferTo(File file) method..
MultipartFile.transferTo() method is calling an implementation of javax.servlet.http.Part Which is implemented this way for tomcat 8
#Override
public void write(String fileName) throws IOException {
File file = new File(fileName);
if (!file.isAbsolute()) {
file = new File(location, fileName);
}
try {
fileItem.write(file);
} catch (Exception e) {
throw new IOException(e);
}
}
and this way for jetty 9.2
public void write(String fileName) throws IOException
{
if (_file == null)
{
_temporary = false;
//part data is only in the ByteArrayOutputStream and never been written to disk
_file = new File (_tmpDir, fileName);
BufferedOutputStream bos = null;
try
{
bos = new BufferedOutputStream(new FileOutputStream(_file));
_bout.writeTo(bos);
bos.flush();
}
finally
{
if (bos != null)
bos.close();
_bout = null;
}
}
else
{
//the part data is already written to a temporary file, just rename it
_temporary = false;
File f = new File(_tmpDir, fileName);
if (_file.renameTo(f))
_file = f;
}
}
What's wrong with the Jetty implementation is that is waiting for file name File.getName() and not the absolute path name File.getPath() which is provided by the call of StandardMultipartHttpServletRequest.transferTo(File file)
#Override
public void transferTo(File dest) throws IOException, IllegalStateException {
this.part.write(dest.getPath());
}
Is this a bug ? Note that this occurs since I have upgraded from spring boot 1.2.7 to 1.3.1. The default was Tomcat and now it is Jetty...
Per the javadoc for javax.servlet.http.Part.write(String filename) the filename parameter is ...
The file is created relative to the location as specified in the
MultipartConfig
In the code you referenced in Jetty 9.2, namely this ...
jetty-9.2.14.v20151106 - MultiPartInputStreamParser.write(String fileName)
You'll see that there's 2 possible code paths it takes, the first is the "in memory" path, and the second is "file on disk" approach.
In both cases, when you specify a filename to Part.write(String) that name is relative to your MultiPartConfig.location (a configuration of which you haven't detailed in your question).
The implementation of MultiPartInputStreamParser has a _tmpDir which is configured from the webapp's MultiPartConfig.location.
If you want this to behave properly, would highly recommend you define a MultiPartConfig.location that is appropriate for your application, instead of relying on the container to pick one.
The Tomcat approach of allowing absolute filenames in Part.write(String) is actually not allowed in the servlet spec (mainly as its a security issue that can be used to cause havoc on a system)
Ok, at the moment if you want to get rid of this error you can switch back to Tomcat instead of Jetty.
Put tomcat into your dependencies:
compile('org.springframework.boot:spring-boot-starter-tomcat')
And declare tomcat as container:
#Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}

Behavior differ between running application in IntelliJ and from jar

I have written an application using Jetty as a server. I wish to package my application into a .jar, so I may put it in a Docker container later on.
When I run my application through IntelliJ, everything seems to be working as it should, and I can reach my index.html from localhost:2222. However, when I package my application (using mvn clean compile assembly:single) and run my jar by typing java -jar myapplication.jar in the shell, my index.html on localhost:2222 returns HTTP Error 404.
My App.java:
public class App {
public static void main( String[] args ) throws Exception {
ResourceConfig config = new ResourceConfig();
config.packages("mypackage");
ServletHolder servlet = new ServletHolder(new ServletContainer(config));
Server server = new Server(2222);
ResourceHandler handler = new ResourceHandler();
handler.setBaseResource(org.eclipse.jetty.util.resource.Resource
.newClassPathResource("index.html"));
ServletContextHandler context = new ServletContextHandler(server, "/*");
context.addServlet(servlet, "/*");
HandlerList handlers = new HandlerList();
handlers.addHandler(handler);
handlers.addHandler(context);
server.setHandler(handlers);
try {
server.start();
server.join();
} finally {
server.destroy();
}
}
}
When running the application in IntelliJ and as jar, I get the following output (may be of interest?):
2015-11-06 09:25:00.991:INFO::main: Logging initialized #279ms
2015-11-06 09:25:01.238:WARN:oejsh.ContextHandler:main: o.e.j.s.ServletContextHandler#490d6c15{/,null,null} contextPath ends with /*
2015-11-06 09:25:01.238:WARN:oejsh.ContextHandler:main: Empty contextPath
2015-11-06 09:25:01.250:INFO:oejs.Server:main: jetty-9.2.3.v20140905
Nov 06, 2015 9:25:01 AM org.glassfish.jersey.server.ApplicationHandler initialize
INFO: Initiating Jersey application, version Jersey: 2.7 2014-03-12 18:11:31...
2015-11-06 09:25:02.294:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler#490d6c15{/,null,AVAILABLE}
2015-11-06 09:25:02.306:INFO:oejs.ServerConnector:main: Started ServerConnector#268f106e{HTTP/1.1}{0.0.0.0:2222}
2015-11-06 09:25:02.306:INFO:oejs.Server:main: Started #1688ms
I am not posting my file of Jersey-methods, since I don't think it is relevant for my question.
So, what is the magic of packaging this up to a jar that can recognize my index.html at localhost:2222? Is it some environment settings in IntelliJ?
Cheers
First of all, don't mix ResourceHandler and ServletContextHandler, you are duplicating static content effort (3 times over with Jersey in play) and will get unreliable results.
Drop the ResourceHandler, set the required ServletContextHandler.setBaseResource(), and add a DefaultServlet to your context.
// Figure out what path to serve content from
ClassLoader cl = App.class.getClassLoader();
// We look for a file, as ClassLoader.getResource() is not
// designed to look for directories (we resolve the directory later)
URL f = cl.getResource("static-root/index.html");
if (f == null)
{
throw new RuntimeException("Unable to find resource directory");
}
// Resolve file to directory
URI webRootUri = f.toURI().resolve("./").normalize();
System.err.println("Main Base Resource is " + webRootUri);
// Setup the basic application "context" for this application at "/"
// This is also known as the handler tree (in jetty speak)
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.setBaseResource(Resource.newResource(webRootUri));
server.setHandler(context);
// Jersey setup
ResourceConfig config = new ResourceConfig();
config.packages("mypackage");
context.addServlet(new ServletHolder(new ServletContainer(config)), "/*");
// Lastly, the default servlet for root content (always needed, to satisfy servlet spec)
// It is important that this is last.
ServletHolder holderDef = new ServletHolder("default",DefaultServlet.class);
holderDef.setInitParameter("dirAllowed","true");
context.addServlet(holderDef,"/");
Note: since you setup Jersey at the url-pattern /* you should know that Jersey is responsible for serving all static content, not Jetty.

Remote PropertySource

Has anyone had any luck constructing a PropertySource that uses a remote source (for example a database) from which to retrieve property values. The idea would be to construct a PropertySource (needs some connection information such as host/port) and plug that into a PropertySourcePlaceholderConfigurer.
The problem seems to be a chicken and egg problem. How can I get the connection information down to the PropertySource? I could first instantiate the PropertySourcePlaceholderConfigurer with configuration to load a property file with the remote host and port properties and then later instantiate the PropertySource and inject that back into the configurer. However, I can't seem to figure a way to ensure that the very first bean to be instantiated (and quickly injected into the configurer) is my property source. I need to have this because, of course, all my other beans depend on the remote properties.
Commons Configuration supports loading properties from a variety of sources (including JDBC Datasources) into a org.apache.commons.configuration.Configuration object via a org.apache.commons.configuration.ConfigurationBuilder.
Using the org.apache.commons.configuration.ConfiguratorConverter, you can convert the Configuration object into a java.util.Properties object which can be passed to the PropertySourcesPlaceholderConfigurer.
As to the chicken and egg question of how to configure the ConfigurationBuilder, I recommend using the org.springframework.core.env.Environment to query for system properties, command-line properties or JNDI properties.
In this exampe:
#Configuration
public class RemotePropertyConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer(Environment environment)
throws Exception {
final PropertySourcesPlaceholderConfigurer props = new PropertySourcesPlaceholderConfigurer();
final ConfigurationBuilder configurationBuilder = new DefaultConfigurationBuilder(environment.getProperty("configuration.definition.file"));
props.setProperties(ConfigurationConverter.getProperties(configurationBuilder.getConfiguration()));
return props;
}
You will need to specify the environment property configuration.definition.file which points to a file needed to configure Commons Configuration:
Similar to Recardo's answer above, I used Spring's PropertiesLoaderUtils instead of Apache's, but it amounts to the same thing. It's not exactly ideal.. hard coded dependency injection, but hey, it works!
/**
* This method must remain static as it's part of spring's initialization effort.
* #return
**/
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
String dbHost = null;
Integer dbPort = null;
// check system / environment properties first
Environment environment = new StandardEnvironment();
if (environment.containsProperty(DB_HOST_KEY)) {
dbHost = environment.getProperty(DB_HOST_KEY);
}
if (environment.containsProperty(DB_PORT_KEY)) {
dbPort = Integer.valueOf(environment.getProperty(DB_PORT_KEY));
}
if (dbHost == null || dbPort == null) {
// ok one or (probably) both properties null, let's go find the database.properties file
Properties dbProperties;
try {
dbProperties = PropertiesLoaderUtils.loadProperties(new EncodedResource(new ClassPathResource("database.properties"), "UTF-8"));
}
catch (IOException e) {
throw new RuntimeException("Could not load database.properties. Please confirm the file is in the classpath");
}
if (dbHost == null) {
dbHost = dbProperties.getProperty(DB_HOST_KEY);
}
if (dbPort == null) {
dbPort = Integer.valueOf(dbProperties.getProperty(DB_PORT_KEY));
}
}
PropertySourceService propertySourceService = new DBPropertySourceService(dbHost, dbPort);
PropertySource<PropertySourceService> propertySource = new DBPropertySource(propertySourceService);
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(propertySource);
configurer.setPropertySources(propertySources);
return configurer;
}
per request, here is the source of the remote property source. It depends on a 'service' class that might do.. well.. anything.. remote access of a property over a socket, talk to a database, whatever.
/**
* Property source for use with spring's PropertySourcesPlaceholderConfigurer where the source is a service
* that connects to remote server for property values.
**/
public class RemotePropertySource extends PropertySource<PropertySourceService> {
private final Environment environment;
/**
* Constructor...
* #param name
* #param source
**/
public RemotePropertySource(PropertySourceService source) {
super("RemotePropertySource", source);
environment = new StandardEnvironment();
}
/* (non-Javadoc)
* #see org.springframework.core.env.PropertySource#getProperty(java.lang.String)
*/
#Override
public Object getProperty(String name) {
// check system / environment properties first
String value;
if (environment.containsProperty(name)) {
value = environment.getProperty(name);
}
else {
value = source.getProperty(name);
}
return value;
}
}

Grizzly, sharing spring generated context

I have a standalone spring project and i need to start an embedded rest service with it.
I could be able to start the server with grizzly, my problem is, when i start grizzly server, it creates its own application context. so the instances created by my parent application is not accessible through the REST service.
Is there anyway of sharing the parent application's context between Grizzly server and parent application, other than getting grizzly generated application context.
This is my code for starting the grizzly server.
public class RemotingServer {
private HttpServer httpServer;
private String host;
private int port;
public RemotingServer(String host, int port) {
this.host = host;
this.port = port;
}
public void init() throws Exception {
URI uri = UriBuilder.fromUri("http://" + host + "/").port(port).build();
ResourceConfig rc = new DefaultResourceConfig();
ConfigurableApplicationContext cac =
new ClassPathXmlApplicationContext("classpath:remoting-context.xml");
IoCComponentProviderFactory factory = new SpringComponentProviderFactory(rc, cac);
httpServer = GrizzlyServerFactory.createHttpServer(uri, rc, factory);
httpServer.start();
}
public void stop() {
httpServer.stop();
}
}
I tried setting current context as cac's parent too. Then i got following exception.
java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
thanks.
Try this:
ConfigurableApplicationContext cac =
new ClassPathXmlApplicationContext("classpath:remoting-context.xml");
// Have Spring load the context
cac.refresh();
IoCComponentProviderFactory factory = new SpringComponentProviderFactory(rc, cac);

Resources