I'm running Spring Batch job on Linux (works fine on Windows). It's a simple file existence check but I am unable to get the system to find the path/file when it definitely exists. Does Spring's Resource class somehow automatically know when it's run on Linux or Windows? Is there something else I can do
My job definition
<batch:job id="vendorGoalsJob" restartable="true">
<batch:step id="checkStartFileExists" next="readHeaderFiles">
<batch:tasklet ref="startFileExistsTasklet" />
</batch:step>
more steps....
</batch:job>
<bean id="startFileExistsTasklet"
class="com.blah.blah.StartFileExistsTasklet"
scope="step">
<property name="resource" value="/di/global/Users/my filename with blanks.txt" />
</bean>
The tasklet class:
package com.blah.blah;
import static java.lang.String.format;
import org.apache.log4j.Logger;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.core.io.Resource;
public class StartFileExistsTasklet implements Tasklet {
private static final Logger logger = Logger.getLogger(StartFileExistsTasklet.class);
private Resource resource;
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
if (!resource.isReadable()) {
logger.info(format("Resource %s not found.", resource.getFilename()));
chunkContext.getStepContext().getStepExecution().setTerminateOnly();
}
return RepeatStatus.FINISHED;
}
public void setResource(Resource resource) {
this.resource = resource;
}
}
...And I always get the 'not found message':
2013-09-26 15:47:09,342 handleStep Executing step: [checkStartFileExists] (SimpleStepHandler.java:133)
2013-09-26 15:47:09,352 execute Resource vendor-goal-trigger.txt not found.
2013-09-26 15:47:09,361 isInterrupted Step interrupted through StepExecution
The default RessourceLoader used by Spring loads from the classpath if the protocol is not specified in the ressource URL.
/**
* Default implementation of the {#link ResourceLoader} interface.
* Used by {#link ResourceEditor}, and serves as base class for
* {#link org.springframework.context.support.AbstractApplicationContext}.
* Can also be used standalone.
*
* <p>Will return a {#link UrlResource} if the location value is a URL,
* and a {#link ClassPathResource} if it is a non-URL path or a
* "classpath:" pseudo-URL.
*
* #author Juergen Hoeller
* #since 10.03.2004
* #see FileSystemResourceLoader
* #see org.springframework.context.support.ClassPathXmlApplicationContext
*/
public class DefaultResourceLoader implements ResourceLoader { ... }
You may try using the file: prefix:
<property name="resource" value="file:/di/global/Users/my filename with blanks.txt"/>
Related
I want to create custom CronJob. I followed this tutorial, but unfortunately, I am not able to see my job instance in Backoffice.
*-item.xml
<typegroup name="Jobs">
<itemtype
generate="true"
code="UsersFindCronJob"
extends="CronJob"
jaloclass="de.hybris.training.core.jalo.UsersFindCronJob"
autocreate="true">
<attributes>
<attribute qualifier="firstName" type="java.lang.String">
<modifiers/>
<persistence type="property"/>
</attribute>
</attributes>
</itemtype>
</typegroup>
*spring.xml
<bean id="usersFindJob" class="de.hybris.training.core.job.UsersFindJob"
parent="abstractJobPerformable"/>
UsersFindJob.java
package de.hybris.training.core.job;
import de.hybris.platform.cronjob.enums.CronJobResult;
import de.hybris.platform.cronjob.enums.CronJobStatus;
import de.hybris.platform.servicelayer.cronjob.AbstractJobPerformable;
import de.hybris.platform.servicelayer.cronjob.PerformResult;
import de.hybris.training.core.model.UsersFindCronJobModel;
public class UsersFindJob extends AbstractJobPerformable<UsersFindCronJobModel> {
#Override
public PerformResult perform(UsersFindCronJobModel cronJobModel) {
try {
// Retrieve firstName from the cronJob
String firstName = cronJobModel.getFirstName();
// Display Hello firstName
System.out.println("Hello " + firstName);
// In case of success return result: SUCCESS and status: FINISHED
return new PerformResult(CronJobResult.SUCCESS, CronJobStatus.FINISHED);
} catch(Exception e) {
// In case of exception return result: ERROR and status: ABORTED
return new PerformResult(CronJobResult.ERROR, CronJobStatus.ABORTED);
}
}
}
Autogenerated UsersFindCronJobModel
/*
* ----------------------------------------------------------------
* --- WARNING: THIS FILE IS GENERATED AND WILL BE OVERWRITTEN! ---
* --- Generated at 09.Nis.2018 22:52:22 ---
* ----------------------------------------------------------------
*
* [y] hybris Platform
*
* Copyright (c) 2000-2016 SAP SE
* All rights reserved.
*
* This software is the confidential and proprietary information of SAP
* Hybris ("Confidential Information"). You shall not disclose such
* Confidential Information and shall use it only in accordance with the
* terms of the license agreement you entered into with SAP Hybris.
*
*/
package de.hybris.training.core.model;
import de.hybris.bootstrap.annotations.Accessor;
import de.hybris.platform.core.model.ItemModel;
import de.hybris.platform.cronjob.model.CronJobModel;
import de.hybris.platform.cronjob.model.JobModel;
import de.hybris.platform.servicelayer.model.ItemModelContext;
/**
* Generated model class for type UsersFindCronJob first defined at extension trainingcore.
*/
#SuppressWarnings("all")
public class UsersFindCronJobModel extends CronJobModel
{
/**<i>Generated model type code constant.</i>*/
public static final String _TYPECODE = "UsersFindCronJob";
/** <i>Generated constant</i> - Attribute key of <code>UsersFindCronJob.firstName</code> attribute defined at extension <code>trainingcore</code>. */
public static final String FIRSTNAME = "firstName";
/**
* <i>Generated constructor</i> - Default constructor for generic creation.
*/
public UsersFindCronJobModel()
{
super();
}
/**
* <i>Generated constructor</i> - Default constructor for creation with existing context
* #param ctx the model context to be injected, must not be null
*/
public UsersFindCronJobModel(final ItemModelContext ctx)
{
super(ctx);
}
/**
* <i>Generated constructor</i> - Constructor with all mandatory attributes.
* #deprecated since 4.1.1 Please use the default constructor without parameters
* #param _job initial attribute declared by type <code>CronJob</code> at extension <code>processing</code>
*/
#Deprecated
public UsersFindCronJobModel(final JobModel _job)
{
super();
setJob(_job);
}
/**
* <i>Generated constructor</i> - for all mandatory and initial attributes.
* #deprecated since 4.1.1 Please use the default constructor without parameters
* #param _job initial attribute declared by type <code>CronJob</code> at extension <code>processing</code>
* #param _owner initial attribute declared by type <code>Item</code> at extension <code>core</code>
*/
#Deprecated
public UsersFindCronJobModel(final JobModel _job, final ItemModel _owner)
{
super();
setJob(_job);
setOwner(_owner);
}
/**
* <i>Generated method</i> - Getter of the <code>UsersFindCronJob.firstName</code> attribute defined at extension <code>trainingcore</code>.
* #return the firstName
*/
#Accessor(qualifier = "firstName", type = Accessor.Type.GETTER)
public String getFirstName()
{
return getPersistenceContext().getPropertyValue(FIRSTNAME);
}
/**
* <i>Generated method</i> - Setter of <code>UsersFindCronJob.firstName</code> attribute defined at extension <code>trainingcore</code>.
*
* #param value the firstName
*/
#Accessor(qualifier = "firstName", type = Accessor.Type.SETTER)
public void setFirstName(final String value)
{
getPersistenceContext().setPropertyValue(FIRSTNAME, value);
}
}
Autogenerated GeneratedUsersFindCronJob
/*
* ----------------------------------------------------------------
* --- WARNING: THIS FILE IS GENERATED AND WILL BE OVERWRITTEN! ---
* --- Generated at 09.Nis.2018 22:52:22 ---
* ----------------------------------------------------------------
*
* [y] hybris Platform
*
* Copyright (c) 2000-2016 SAP SE
* All rights reserved.
*
* This software is the confidential and proprietary information of SAP
* Hybris ("Confidential Information"). You shall not disclose such
* Confidential Information and shall use it only in accordance with the
* terms of the license agreement you entered into with SAP Hybris.
*
*/
package de.hybris.training.core.jalo;
import de.hybris.platform.cronjob.jalo.CronJob;
import de.hybris.platform.jalo.Item.AttributeMode;
import de.hybris.platform.jalo.SessionContext;
import de.hybris.training.core.constants.TrainingCoreConstants;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Generated class for type {#link de.hybris.training.core.jalo.UsersFindCronJob UsersFindCronJob}.
*/
#SuppressWarnings({"deprecation","unused","cast","PMD"})
public abstract class GeneratedUsersFindCronJob extends CronJob
{
/** Qualifier of the <code>UsersFindCronJob.firstName</code> attribute **/
public static final String FIRSTNAME = "firstName";
protected static final Map<String, AttributeMode> DEFAULT_INITIAL_ATTRIBUTES;
static
{
final Map<String, AttributeMode> tmp = new HashMap<String, AttributeMode>(CronJob.DEFAULT_INITIAL_ATTRIBUTES);
tmp.put(FIRSTNAME, AttributeMode.INITIAL);
DEFAULT_INITIAL_ATTRIBUTES = Collections.unmodifiableMap(tmp);
}
#Override
protected Map<String, AttributeMode> getDefaultAttributeModes()
{
return DEFAULT_INITIAL_ATTRIBUTES;
}
/**
* <i>Generated method</i> - Getter of the <code>UsersFindCronJob.firstName</code> attribute.
* #return the firstName
*/
public String getFirstName(final SessionContext ctx)
{
return (String)getProperty( ctx, FIRSTNAME);
}
/**
* <i>Generated method</i> - Getter of the <code>UsersFindCronJob.firstName</code> attribute.
* #return the firstName
*/
public String getFirstName()
{
return getFirstName( getSession().getSessionContext() );
}
/**
* <i>Generated method</i> - Setter of the <code>UsersFindCronJob.firstName</code> attribute.
* #param value the firstName
*/
public void setFirstName(final SessionContext ctx, final String value)
{
setProperty(ctx, FIRSTNAME,value);
}
/**
* <i>Generated method</i> - Setter of the <code>UsersFindCronJob.firstName</code> attribute.
* #param value the firstName
*/
public void setFirstName(final String value)
{
setFirstName( getSession().getSessionContext(), value );
}
}
Impex
INSERT_UPDATE UsersFindCronJob ; code[unique=true] ; job(code) ; firstName ; usersFindCronJob ; usersFindJob ; Mouad
I also tried to import that Impex but it said it is not valid.
I think you only missed to update your system. Then you can create the instance of your job using below Impex or through Backoffice.
To update
ant clean all
hybrisserver.bat
Open HAC (https://localhost:9002/hac/)
Go to Platform > Update
Only select Update running system checkbox and essential data of your extension.
If you are in the situation where you can't run essential data(say Production) you need to explicitly run ServiceLayerJob after the update.
Click on update button
Run Impex
INSERT_UPDATE UsersFindCronJob ; code[unique=true] ; job(code) ; firstName ;
; usersFindCronJob ; usersFindJob ; Mouad ;
The situation where you haven't run essential data you need to run below Impex(as mentioned by #Johannes)
INSERT_UPDATE ServicelayerJob;code[unique=true];springId;
;usersFindJob;usersFindJob
Refer https://wiki.hybris.com/display/R5T/Trail+~+CronJobs
First, you need to seperate your impex header from your data by a new line:
INSERT_UPDATE UsersFindCronJob;code[unique=true];job(code);firstName
;usersFindCronJob;usersFindJob;Mouad
Then you also missed the part where you create the Job itself:
INSERT_UPDATE ServicelayerJob;code[unique=true];springId;
;usersFindJob;usersFindJob
When you create your custom cronjob,then you need to update the "Custom Cronjob" you created.
****What mistake we do is updating these below statements when we write custom cronjob:****
INSERT_UPDATE ServicelayerJob;code[unique=true];springId;
INSERT_UPDATE Cronjob;code[unique=true];job(code);singleExecutable;sessionLanguage(isocode)
****Actually we need to write these below statements:****
INSERT_UPDATE ServicelayerJob;code[unique=true];springId;
INSERT_UPDATE ;code[unique=true];job(code);singleExecutable;sessionLanguage(isocode)
Please note that,if the attributes you added in custom cronjob are mandatory then you need to update those attributes as well,like:
INSERT_UPDATE ;code[unique=true];job(code);singleExecutable;sessionLanguage(isocode);
Can anyone please help me with this JAXB unmarshal issue?
So, this is the file employee.xml I am using:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<parent>
<anotherchild1>Another</anotherchild1>
</parent>
and this is the Parent.java class which has JAXB annotation:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package XMLToObject;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* #author Gauravb
*/
#XmlRootElement
public class Parent {
private String child;
private String anotherchild1;
public String getAnotherChild1() {
return anotherchild1;
}
public void setAnotherChild1(String child) {
this.anotherchild1 = anotherchild1;
}
}
And this is XMLToObject.java file which I am using:
package XMLToObject;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author Gauravb
*/
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class XMLToObject {
public static void main(String[] args) {
try {
File file = new File("employee.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Parent.class);
System.out.println("Reading....."+file.getAbsolutePath());
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Parent p = (Parent) jaxbUnmarshaller.unmarshal(file);
//System.out.println("Child:"+p.getChild());
System.out.println("Another Child:"+p.getAnotherChild1());
} catch (JAXBException e) {e.printStackTrace(); }
}
}
Now, my issue is when I run XMLToObject.java file, then I see following:
Reading.....C:\xxxxxx\employee.xml
Another Child:null
So, can anyone please let me know what can be the reason behind the value as null?
Please note:
1. I am a newbie in JAXB
2. I am using Netbeans editor while running the code
3. I do NOT have XSD for the XML and dont want to use any for my learning
4. I do NOT want to use MOXy for now, this is something I will learn next
You have forgot to add #XmlElement annotation in the binding class Parent.java as below
#XmlElement
private String anotherchild1;
I'm new to CXF and JAXB. I'm having problems with a RESTful client that calls a external web service. I think I've followed the steps necessary to do this but I'm getting the following error when executing the client:
SEVERE: No message body reader has been found for class com.jaxb.AcXML, ContentType: text/html
Exception in thread "main" javax.ws.rs.client.ResponseProcessingException: No message body reader has been found for class com.jaxb.AcXML, ContentType: text/html
at org.apache.cxf.jaxrs.impl.ResponseImpl.reportMessageHandlerProblem(ResponseImpl.java:433)
at org.apache.cxf.jaxrs.impl.ResponseImpl.doReadEntity(ResponseImpl.java:384)
at org.apache.cxf.jaxrs.client.AbstractClient.readBody(AbstractClient.java:512)
at org.apache.cxf.jaxrs.client.WebClient.handleResponse(WebClient.java:1173)
at org.apache.cxf.jaxrs.client.WebClient.doResponse(WebClient.java:1156)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1092)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)
at org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:428)
at org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:611)
at com.ws.GetOpenPO.getOpenPOs(GetOpenPO.java:58)
at com.ws.GetOpenPO.main(GetOpenPO.java:79)
My IDE is Eclipse Indigo. I've created a Dynamic Web Project and included the CXF 2.x Web Services, JAX-RS and JAXB facets. Below is the class I'm testing with:
package com.ws;
import java.text.MessageFormat;
import javax.ws.rs.core.MediaType;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import com.jaxb.AcXML;
public class GetOpenPO {
private String baseUrl;
public AcXML getOpenPOs( )
{
// Create a WebClient pointing to the base URL of the RESTful web service
WebClient client = WebClient.create( baseUrl ).path("GetOpenPOs");
HTTPConduit http = WebClient.getConfig(client).getHttpConduit();
HTTPClientPolicy httpClientPolicy=new HTTPClientPolicy();
httpClientPolicy.setReceiveTimeout(1000000);
httpClientPolicy.setConnectionTimeout(1000000);
httpClientPolicy.setProxyServer("proxy1.global.mycompany.com");
httpClientPolicy.setProxyServerPort(8080);
http.setClient(httpClientPolicy);
// Set the path from which we wish to get the object, request XML, and use JAXB
AcXML POs= client.accept(MediaType.APPLICATION_XML)
.query("U", "parm1")
.query("P", "parm2")
.query("N", "parm3")
.query("Processed", "parm4")
.query("StationEnd", "")
.get( AcXML.class);
return POs;
}
public String getBaseUrl()
{
return baseUrl;
}
public void setBaseUrl( String baseUrl )
{
this.baseUrl = baseUrl;
}
public static void main(String args[]){
GetOpenPO x = new GetOpenPO ();
x.setBaseUrl("http://www24.externalws.net/webservices/webservices.asmx");
AcXML openPOs = x.getOpenPOs();
}
}
I was able to call the webservice via my browser, which returns an XML document. Using this XML I created a schema and then using the "JAXB Classes from Schema" wizard in Eclipse the JAXB objects were generated. Below is the source file for the AcXML entity that I'm trying to retrieve via the client:
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// See http://java.sun.com/xml/jaxb
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2015.08.05 at 10:18:48 AM EDT
//
package com.jaxb;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element ref="{https://www.externalws.net/acXMLSchema.xsd}Header"/>
* <element ref="{https://www.externalws.net/acXMLSchema.xsd}Request"/>
* </sequence>
* <attribute name="lang" use="required">
* <simpleType>
* <restriction base="{http://www.w3.org/2001/XMLSchema} string">
* </restriction>
* </simpleType>
* </attribute>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"header",
"request"
})
#XmlRootElement(name = "acXML")
public class AcXML {
#XmlElement(name = "Header", required = true)
protected Header header;
#XmlElement(name = "Request", required = true)
protected Request request;
#XmlAttribute(name = "lang", required = true)
protected String lang;
/**
* Gets the value of the header property.
*
* #return
* possible object is
* {#link Header }
*
*/
public Header getHeader() {
return header;
}
/**
* Sets the value of the header property.
*
* #param value
* allowed object is
* {#link Header }
*
*/
public void setHeader(Header value) {
this.header = value;
}
/**
* Gets the value of the request property.
*
* #return
* possible object is
* {#link Request }
*
*/
public Request getRequest() {
return request;
}
/**
* Sets the value of the request property.
*
* #param value
* allowed object is
* {#link Request }
*
*/
public void setRequest(Request value) {
this.request = value;
}
/**
* Gets the value of the lang property.
*
* #return
* possible object is
* {#link String }
*
*/
public String getLang() {
return lang;
}
/**
* Sets the value of the lang property.
*
* #param value
* allowed object is
* {#link String }
*
*/
public void setLang(String value) {
this.lang = value;
}
}
Can someone tell me what I'm doing wrong or what am I missing that's causing the client to fail?
Thank You.
Add JAXBProvider
List<JAXBElementProvider> list = new ArrayList<JAXBElementProvider>();
list.add(new JAXBElementProvider());
WebClient client = WebClient.create( baseUrl, list).path("GetOpenPOs");
I have tried to generate java classes from a schema xsd with JAXB2.1 and run XJC and it works.
I have included the schema in a wsdl file and i generate java classes with wsdl2java command using CXF.
The problem is abouta java class where there are difference:
The difference is the content attribute and its getter and setter which is missing with wsdl2java command.
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.1-b02-fcs
// See http://java.sun.com/xml/jaxb
// Any modifications to this file will be lost upon recompilation of the source schema.
//
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "BIN", **propOrder = {
"content"**
})
#XmlSeeAlso({
ED.class
})
public abstract class BIN {
**#XmlValue
protected String content;**
#XmlAttribute
protected BinaryDataEncoding representation;
/**
public String getContent() {
return content;
}
/**
*
* Binary data is a raw block of bits. Binary data is a
* protected type that MUST not be used outside the data
* type specification.
*
*
* #param value
* allowed object is
* {#link String }
*
*/
**public void setContent(String value) {
this.content = value;
}**
/**
* Gets the value of the representation property.
*
* #return
* possible object is
* {#link BinaryDataEncoding }
*
*/
public BinaryDataEncoding getRepresentation() {
if (representation == null) {
return BinaryDataEncoding.TXT;
} else {
return representation;
}
}
/**
* Sets the value of the representation property.
*
* #param value
* allowed object is
* {#link BinaryDataEncoding }
*
*/
public void setRepresentation(BinaryDataEncoding value) {
this.representation = value;
}
}
I need this attribute into this class.
Is there a way to do this like add a parameter?
This is my wsdl2java command:
call wsdl2java -Debug -verbose -exsh true -autoNameResolution -p %PACKAGE_BASE%.pa -p "urn:hl7-org:v3"=%PACKAGE_BASE%.patient.hl -d %PROJECT_HOME%\src\main\java\ %WSDL_HOME%\Test.wsdl
thanks
We'd like to set facelets.development to false to suppress stack traces in non-development environments, but we'd like to have it set to true in dev for debugging.
Our deployment process dictates one CI build that is migrated through the environments up to production, so we can't use an approach that requires rebuilding the app / rewriting web.xml for each environment. We'd like to change the value from the application, based on a properties file setting. Is this possible? How can the application access facelets.development?
I think the simplest approach is to put the Context parameter in web.xml:
<context-param>
<param-name>facelets.DEVELOPMENT</param-name>
<param-value>false</param-value>
</context-param>
and override it in your Development deployments. This is usually possible without changing the WAR. In Tomcat, include META-INF/context.xml in your WAR with this line (within <Context> ... </Context>):
<Parameter name="facelets.DEVELOPMENT" value="true" override="false" />
Tomcat will copy this file on startup to $CATALINA_BASE/conf/[enginename]/[hostname]/[context-path-name].xml which can be used to configure the webapp outside the
WAR. This will happen in each of your environments and admins have to change it only once to:
<Parameter name="facelets.DEVELOPMENT" value="false" override="false" />
Afterwards, Tomcat will not overwrite it even if a new WAR with a newer /META-INF/context.xml is deployed. The names of context Parameters have to match the declarations in WEB-INF/web.xml.
See http://tomcat.apache.org/tomcat-6.0-doc/config/context.html for details ("Introduction" and "Context Parameters" section).
I can think of a few ways to do this, none of them very pleasant.
Decorate the FacesContext to control the init parameters programmatically. This is a lot of work for so little gain.
Patch the FaceletViewHandler class to get the behaviour you want. This may add maintenance overhead if you upgrade your Facelets libs. May make the folks who manage the app in production unhappy.
A variation on the patch approach is to just use the patched JARs in your dev/test machines and put them in the server libs - then use PARENT_FIRST classloading to load them over JARs in the apps (assuming your app server supports all that). The downside of this is that it imposes classloading policies and you have to manage JARs all over the place.
I would favour some other approach. If this setting is required on test machines, perhaps a deployment script could modify the app during the install for those servers. If you want it set to true in your source control, the build script could remove it as part of the build process. This approach would have no impact on your runtime code.
I've implemented a variation on option 1 above. eg
wrote a dynamic proxy for a ServletContext that intercepts the getInitParameter and getInitParameterNames methods returning appropriate values (sourced from environment-specific property files in this case)
wrote a very small subclass of FacesContextFactoryImpl that proxies the first/servletcontext parameter to getFacesContext and then delegates to the superclass.
added a faces-context-factory clause to my faces-config naming my FacesContextFactoryImpl class
I already had a mechanism in place to load environment-specific property files and make them available as a Properties object to the application (the factory in point 2 passes these properties to the proxy in point 1 to use as an alternate source of initParameter values)
The proxy looks like:
package zzzzz.framework.context;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A proxy for ServletContext that intercepts accesses to the initParameters and
* returns values from the specified params instead. Generally useful if we have
* a set of properties (eg SystemContext.getInstance().getConfigProperties()) that
* we want to use in preference to the webapp initProperties.
*
*
*/
public class ServletContextProxy
implements InvocationHandler {
#SuppressWarnings("unused")
private static final Log log = LogFactory.getLog(ServletContextProxy.class);
#SuppressWarnings("unchecked")
public static ServletContext newInstance(ServletContext subject,
Map params) {
return newInstance(subject,
params,
true);
}
#SuppressWarnings("unchecked")
public static ServletContext newInstance(ServletContext subject,
Map params,
boolean overrideInitValues) {
return (ServletContext) Proxy.newProxyInstance(subject.getClass()
.getClassLoader(),
subject.getClass()
.getInterfaces(),
new ServletContextProxy(subject,
params,
overrideInitValues));
}
/**
* A convenience method to help extracting the initParameters from a
* ServletContext because it doesn't expose it's underlying Map
*
* #param config
* #return
*/
#SuppressWarnings("unchecked")
protected static Map copyInitParameters(Map parms,
ServletContext config) {
Enumeration names = config.getInitParameterNames();
// copy all the existing initParameters
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
parms.put(name,
config.getInitParameter(name));
}
return parms;
}
private boolean overrideInitValues = true;
#SuppressWarnings("unchecked")
private Map params;
private ServletContext subject;
#SuppressWarnings("unchecked")
public ServletContextProxy(ServletContext subject,
Map params,
boolean overrideInitValues) {
this.subject = subject;
this.overrideInitValues = overrideInitValues;
this.params = new Hashtable();
if (this.overrideInitValues) { // default behaviour... supplied parameters win
// start with initParameters
copyInitParameters(this.params,
subject);
// override and supplement with supplied params
if (params != null) {
this.params.putAll(params);
}
} else {
// start with supplied params
if (params != null) {
this.params.putAll(params);
}
// override and supplement with initParameters
copyInitParameters(this.params,
subject);
}
}
public Object invoke(Object proxy,
Method m,
Object[] args) throws Throwable {
Object result;
try {
if ("getInitParameter".equals(m.getName())) {
result = this.params.get(args[0]);
} else if ("getInitParameterNames".equals(m.getName())) {
result = IteratorUtils.asEnumeration(this.params.keySet()
.iterator());
} else {// else let it go through to the keeper
result = m.invoke(this.subject,
args);
}
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: "
+ e.getMessage());
}
return result;
}
}
The factory looks like:
package zzz.faces.context;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.lifecycle.Lifecycle;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import zzzzz.framework.context.ServletContextProxy;
import zzzzz.context.SystemContext;
/**
* A FacesContextFactory implementation that supplements/overrided the
* servletContext initParemeters with properties form
* SystemContext.configProperties
* <p>
* The point of this is that it allows us to substitute configuration in the
* web.xml like this (which requires rewriting web.xml to change)
* </p>
*
* <pre>
* <!-- Enables special Facelets debug output during development -->
* <context-param>
* <param-name>facelets.DEVELOPMENT</param-name>
* <param-value>true</param-value>
* </context-param>
* </pre>
*
* <p>
* with settings in the relevent application.properties file like this (which
* can be changed separately to the webapp)
* </p>
*
* <pre>
* # Enables special Facelets debug output during development
* facelets.DEVELOPMENT=true
* </pre>
*
* <p>
* usage: add a clause to faces-config like this:
*
* <pre>
* <factory>
* <faces-context-factory>zzzzz.faces.context.FacesContextFactoryImpl</faces-context-factory>
* </factory>
* </pre>
* <p>
*
*/
public class FacesContextFactoryImpl extends com.sun.faces.context.FacesContextFactoryImpl {
#SuppressWarnings("unused")
private static final Log log = LogFactory.getLog(FacesContextFactoryImpl.class);
public FacesContextFactoryImpl() {
super();
}
#Override
public FacesContext getFacesContext(Object sc,
Object request,
Object response,
Lifecycle lifecycle) throws FacesException {
if (sc instanceof ServletContext
&& !(sc instanceof ServletContextProxy)) {
// wrap the servlet context with a proxy to override/supplement initParameters
sc = ServletContextProxy.newInstance((ServletContext) sc,
SystemContext.getInstance()
.getConfigProperties(),
true);
}
return super.getFacesContext(sc,
request,
response,
lifecycle);
}
}
and the faces-config looks like
<faces-config>
blah waffle....
<factory>
<faces-context-factory>zzzz.faces.context.FacesContextFactoryImpl</faces-context-factory>
</factory>
</faces-config>
What SystemContext.getInstance().getConfigProperties() looks like is an excercise for another day but it just returns the Map of property values the app is supposed to use