i´m trying to setup a JSF application with Spring Web Flow.
I´m using Spring 3.1
Spring faces 2.3.1
JSF (api,impl) 2.1.6
My problem is the following that the program is not taking the correct
location of my jsf views.
I have found some tutorials like
or http://dgparsons.org/jsf-and-spring-webflow.html
for integrating jsf and spring web flow.
In one of these view resolving is done with:
<bean id="faceletsViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.faces.mvc.JsfView"/>
<property name="prefix" value="/WEB-INF/views" />
<property name="suffix" value=".xhtml" />
</bean>
And so I put my xhtml files in the folder /WEB-INF/views/
My Flow is in the directory /WEB-INF/config/flow/start.flow
When I gave it a try the program always wants to open the start.xhtml
from the folder /WEB-INF/config/flow/start.xhtml
So it seems it is completly ignoring the UrlBasedViewResolver.
My FlowBuilderServices is configured as following:
<beans:bean id="flowBuilderServices"
class="org.springframework.webflow.engine.builder.support.FlowBuilderServices">
...
<beans:property name="viewFactoryCreator">
<beans:bean
class="org.springframework.faces.webflow.JsfViewFactoryCreator" />
</beans:property>
...
</beans:bean>
In the tutorials there is no JsfViewFactoryCreator and I cant find a tutorial about it.
As from the javadoc JsfViewFactoryCreator/JsfViewFactory is responsible for the RESTORE_VIEW phase.
So I dived into the source code of the getView() Method of the JsfViewFactory.
JsfViewFactory.java
In line 105
UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewName);
a FlowViewHandler is called, which generates the wrong path relative to the path of my flow files.
resourcePath = resolveResourcePath(RequestContextHolder.getRequestContext(), viewId);
So my question is now. How can I associate the UrlBasedViewResolver correctly with the flowRegistry/flowbuilderservices so that it takes /WEB-INF/views/*.xthml as my path to the xhtml files.
Or if it is maybe old fashioned to use UrlBasedViewResolver (as I didnt see it in the spring web flow documentation for jsf) how can the webflow beans be configured correctly to use the directories i defined.
For completeness here is my webflow-config.xml file
<!-- Executes flows: the central entry point into the Spring Web Flow system -->
<flow-executor id="flowExecutor">
<flow-execution-listeners>
<listener ref="facesContextListener" />
<listener ref="securityListener" />
</flow-execution-listeners>
</flow-executor>
<!-- The registry of executable flow definitions -->
<flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices"
base-path="/WEB-INF/config/flow">
<flow-location-pattern value="/*.xml" />
</flow-registry>
<beans:bean id="flowBuilderServices"
class="org.springframework.webflow.engine.builder.support.FlowBuilderServices">
<beans:property name="expressionParser">
<beans:bean
class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
<beans:constructor-arg>
<beans:bean class="com.sun.el.ExpressionFactoryImpl" />
</beans:constructor-arg>
</beans:bean>
</beans:property>
<beans:property name="viewFactoryCreator">
<beans:bean
class="org.springframework.faces.webflow.JsfViewFactoryCreator" />
</beans:property>
<beans:property name="conversionService">
<beans:bean
class="org.springframework.binding.convert.service.DefaultConversionService">
</beans:bean>
</beans:property>
</beans:bean>
So finally I found the way to it myself and it was quite simple.
As written in the documentation of spring webflow under - Specifying view identifiers
Spring Webflow Documentation
the solution is to use an absolute view identifier in the flow definition:
<view-state id="enterBookingDetails" view="/WEB-INF/hotels/booking/bookingDetails.xhtml">
Thus there is no need for a URLBasedViewResolver or a special configuration of JSFViewFactoryCreator.
Related
I'm trying to learn Spring Integration and for this i would like to create an application like this:
From Oracle i send messages (on Oracle Queue), this message will be intercepted from a Java application (build with Spring Integration) and the application will send an email based on message received. The message will contain To: - Cc: and the text to send.
To make this kind of communication i've decided to use JMS (i think in Oracle this is made with Oracle AQ).
In the database i've already created the Queue and now i'm trying to create a simple applicationContext.xml to start this handshake.
Looking on the net i've found really few articles about this (Spring Integration + Oracle AQ) and i'm getting some error. The main error is this: java.lang.ClassNotFoundException: oracle.jms.AQjmsFactory
Right now this is my applicationContext.xml
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:orcl="http://www.springframework.org/schema/data/orcl"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
http://www.springframework.org/schema/data/orcl http://www.springframework.org/schema/data/orcl/spring-data-orcl-1.0.xsd">
<int:channel id="inbound" />
<int:channel id="outbound" />
<bean id="simpleMessageListener" class="it.dirimo.SimpleMessageListener" />
<int-jms:inbound-channel-adapter
channel="inbound"
connection-factory="connectionFactory"
destination-name="Q1">
<int:poller fixed-delay="1000" />
</int-jms:inbound-channel-adapter>
<int:service-activator input-channel="inbound" output-channel="outbound" ref="simpleMessageListener" method="onMessage" />
<int-jms:outbound-channel-adapter id="jmsOut"
channel="outbound"
connection-factory="connectionFactory"
destination-name="sampleQueue" />
<int:poller id="poller" default="true" fixed-delay="10000" />
<orcl:aq-jms-connection-factory id="connectionFactory"
data-source="dataSource"
use-local-data-source-transaction="true"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" lazy-init="false">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:ORCL" />
<property name="username" value="user" />
<property name="password" value="password" />
</bean>
</beans>
Maybe i'm using "old" technologies (for example i've seen for the first time this org.apache.commons.dbcp.BasicDataSource)
Unfortunally i'm so new about Spring Integration and i've seen for the first time Oracle Queue (i'm using Oracle for work but never used any kind of Queue).
Some advice of how to proceed will be apreciated :)
EDIT 1
To solve the problem about the AQjmsFactory need to include aqapi.jar
java.lang.ClassNotFoundException: oracle.jms.AQjmsFactory
This simply means you are missing the jar that contains that class from the classpath.
Oracle, typically, requires that you manually download their jars from them directly.
I have JSF 2.2 application deployed on glassfish.
I wan't to beautify the URL
http://MyWebsite/test/SOMEFOLDER/login.xhtml
to be
http://MyWebsite/test/login.xhtml
I am using pretty faces using the follow configuration
<url-mapping id="login">
<pattern value="/login.xhtml" />
<view-id value="/SOMEFOLDER/login.xhtml" />
<!-- Non JSF View Id -->
</url-mapping>
I am not adding anything in the web.xml
First, I have just started looking at Spring Integration today, so I have very little experience. I already have a basic scheduled ftp file parser setup using spring integration:
<int:channel id="ftpIn" />
<int-ftp:inbound-channel-adapter
channel="ftpIn"
session-factory="ftpClientFactory"
filename-pattern="*.xml"
local-directory="${TEMP_DIR}">
<int:poller fixed-rate="${ftp.polling.rate}" />
</int-ftp:inbound-channel-adapter>
<bean id="ftpClientFactory" class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${ftp.host}" />
<property name="port" value="${ftp.port}" />
<property name="username" value="${ftp.username}" />
<property name="password" value="${ftp.password}" />
</bean>
<int:service-activator
input-channel="ftpIn"
method="handle"
ref="ftpInHandler" />
<bean id="ftpInHandler" class="thanks.for.looking.FtpInHandler" />
This works; however, I want to add additional functionality that checks (at a fixed-rate) if the system is ready before starting the scheduled (fixed-rate) ftp adapter. I am stuck on the best way to implement this. Any help or guidance is appreciated.
Best Regards,
Jared
<poller> has an option like <advice-chain>.
So you just need to write some custom Advice:
public class CheckSystemInterceptor implements MethodInterceptor {
Object invoke(MethodInvocation invocation) throws Throwable {
return mySystem.isOK() ? invocation.proceed() : null;
}
}
Configure it as a <bean> with your system checker and inject it into that <advice-chain>.
It will be invoked on each poll.
I been trying to add some JSF(Face) code into my Spring Webflow project but I am getting the following error:
java.lang.IllegalStateException: FacesContext has not been initialized within the current Web Flow request. Check the configuration for your <webflow:flow-executor>. For JSF you will need FlowFacesContextLifecycleListener configured as one of its flow execution listeners.
Here is my flow.xml file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xmlns:faces="http://www.springframework.org/schema/faces"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd
http://www.springframework.org/schema/faces
http://www.springframework.org/schema/faces/spring-faces-2.2.xsd">
<!-- Executes flows: the central entry point into the Spring Web Flow system -->
<webflow:flow-executor id="flowExecutor">
<webflow:flow-execution-listeners>
<webflow:listener ref="facesContextListener"/>
</webflow:flow-execution-listeners>
</webflow:flow-executor>
<!-- The registry of executable flow definitions -->
<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" base-path="/WEB-INF">
<webflow:flow-location-pattern value="**/*-flow.xml" />
</webflow:flow-registry>
<!-- Configures the Spring Web Flow JSF integration -->
<faces:flow-builder-services id="flowBuilderServices" />
<!-- A listener maintain one FacesContext instance per Web Flow request. -->
<bean id="facesContextListener"
class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener" />
</beans>
and here is my flow:
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<var name="customer" class="org.smith.webflowtemplate.domain.Customer"/>
<view-state id="checkbox-input" model="customer" >
<transition on="submit" to="preview" validate="false"/>
<transition on="cancel" to="thanks" validate="false"/>
</view-state>
<view-state id="preview" model="customer">
<transition on="accept" to="endState"/>
</view-state>
<end-state id="endState" view="/index.jsp" />
</flow>
You need to add a Faces Context listener like this:
<flow-executor id="flowExecutor">
<flow-execution-listeners>
<listener ref="facesContextListener" />
</flow-execution-listeners>
</flow-executor>
<beans:bean id="facesContextListener" class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener" />
I have been POC'ing with milestone 2 of the Kerberos extension to achieve single sign-on.
Quick summary of my setup:
KDC: Windows Server 2003 (SP2)
Web server: Ubuntu 10.04, Tomcat 5.5, Java 1.6.0_22 (not on the domain)
Spring: Framework 3.0.5, Security 3.0.4, Kerberos Extension 1.0.0 M2
I have setup my configuration to first attempt SPNEGO authentication and if it fails to then redirect to a login page.
This is done by setting SpnegoAuthenticationProcessingFilter's "failureHandler" property. I have successfully tested this
on Windows machines (XP and 7) that are in and out of the domain. The machines that are outside the domain gets redirected to
the login page and then can successfully login.
Here is my config:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<http entry-point-ref="spnegoEntryPoint" auto-config="false">
<intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/j_spring_security_check*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
<form-login login-page="/login.html" default-target-url="/" always-use-default-target="true"/>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="kerberosServiceAuthenticationProvider" />
<authentication-provider ref="kerberosAuthenticationProvider"/>
</authentication-manager>
<beans:bean id="spnegoEntryPoint"
class="org.springframework.security.extensions.kerberos.web.SpnegoEntryPoint" />
<beans:bean id="spnegoAuthenticationProcessingFilter"
class="org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter">
<beans:property name="failureHandler">
<beans:bean class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.html" />
<beans:property name="allowSessionCreation" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="kerberosServiceAuthenticationProvider"
class="org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider">
<beans:property name="ticketValidator">
<beans:bean
class="org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator">
<beans:property name="servicePrincipal" value="HTTP/mywebserver.corpza.corp.co.za"/>
<beans:property name="keyTabLocation" value="classpath:mywebserver.keytab" />
<beans:property name="debug" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="userDetailsService" ref="dummyUserDetailsService" />
</beans:bean>
<beans:bean id="kerberosAuthenticationProvider" class="org.springframework.security.extensions.kerberos.KerberosAuthenticationProvider">
<beans:property name="kerberosClient">
<beans:bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosClient">
<beans:property name="debug" value="true" />
</beans:bean>
</beans:property>
<beans:property name="userDetailsService" ref="dummyUserDetailsService" />
</beans:bean>
<beans:bean class="org.springframework.security.extensions.kerberos.GlobalSunJaasKerberosConfig">
<beans:property name="debug" value="true" />
<beans:property name="krbConfLocation" value="/etc/krb5.conf" />
</beans:bean>
<beans:bean id="dummyUserDetailsService" class="main.server.DummyUserDetailsService"/>
</beans:beans>
When the Windows machine is outside the domain my web server responds with the "WWW-Authenticate Negotiate" header (as per usual) to which
the Windows machine responds to with a NTLM header ("Negotiate TlRM..."), where the SpnegoAuthenticationProcessingFilter then says "Negotiate Header was invalid..."
and awesomly redirects the user to the login page. Great.
The Issue:
There are a number of Mac and Linux machines that are permanently outside the domain which would need to use this web app.
When they hit the web app (with Firefox 3.6) my web server responds with the expected "WWW-Authenticate Negotiate" header to inform
the client that the web app is Kerberized, BUT neither the Mac or Linux machines responds at all. Thus the SpnegoAuthenticationProcessingFilter
doesn't get entered again and hence no failure and subsequently no redirection to the login page takes place.
The Question:
Why doesn't the Mac and Linux machines respond in the same way as the Windows machines (can't belive I just asked that...)?
I know that when the Mac and Linux machines obtain a ticket (via kinit) they are able to authenticated but this doesn't seem like a good solution
at all as it requires effort from the user to provide credentials etc. where the tickets expires as well.
So is there any way that we can get these machines to send back a NTLM header as the Windows machines does?
Or if there are any other suggestions/ways please let me know.
B.t.w. I did configure the Firefoxes that I used on to test on the Mac and Linux machines ("network.negotiate-auth.delegation-uris" and "network.negotiate-auth.trusted-uris" was set to ".corpza.corp.co.za").
It doesn't look like you set network.automatic-ntlm-auth.trusted-uris. Did you read through this?
https://developer.mozilla.org/en/Integrated_Authentication
Grant
You are going the wrong way. Don't rely on the failing SPNEGO filter. The Linux and Mac clients behave correctly just as the Windows client does. The general setup should look like the following, if the filter does not implement/support that, you have found a bug.
Client sends a request to the server
Client receives "401 WWW-Authenticate: Negotiate" from the server (or any combination of Negotiate, Basic, Digest).
Client tries to supply auth data and if it fails it shows the 401 page.
Now you rely on faulty/non-processible data sent from the Windows client to present the form. You should rather send the form with the 401 directly to give all clients the ability to choose an appropriate login method (SPNEGO or form, ie. fail-through). Keep in mind that form auth is not an http auth like Negotiate, Digest or Basic. It has to be treated differently.
We are using that filter too and I am not really happy with it. So I have hands-on experience.