I am working on a JSR-286 portlet application that uses JSF 1.2. I am working on moving my JSF managed beans to Spring beans, and I noticed what appears to a difference between how Spring is treating request scope from how JSF is treating request scope.
In my portlet application, I have two portlets that live on the same page and both use the same starting JSF portlet page view. When I use JSF managed request beans, there is an individual request bean created for each portlet, which is the behavior I am looking for. When I use Spring beans, only one request bean is created and is shared among both portlets. Is this normal behavior? Is there any way I can stop it from doing this?
My original faces-config.xml file, before moving my beans to Spring:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<application>
<state-manager>com.ibm.faces.application.DevelopmentStateManager</state-manager>
<variable-resolver>com.ibm.faces.portlet.PortletVariableResolver</variable-resolver>
</application>
<factory>
<faces-context-factory>com.ibm.faces.context.AjaxFacesContextFactory</faces-context-factory>
<render-kit-factory>com.ibm.faces.renderkit.AjaxRenderKitFactory</render-kit-factory>
</factory>
<managed-bean>
<managed-bean-name>sessionBean</managed-bean-name>
<managed-bean-class>sanitycheck.SessionBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>pc_SanityCheckProjectView</managed-bean-name>
<managed-bean-class>sanitycheck.SanityCheckProjectView</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>sessionBean</property-name>
<value>#{sessionBean}</value>
</managed-property>
</managed-bean>
<lifecycle>
<phase-listener>com.ibm.faces.webapp.ValueResourcePhaseListener</phase-listener>
</lifecycle>
</faces-config>
My faces-config.xml file after moving beans to Spring:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<application>
<state-manager>com.ibm.faces.application.DevelopmentStateManager</state-manager>
<variable-resolver>com.ibm.faces.portlet.PortletVariableResolver</variable-resolver>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
</application>
<factory>
<faces-context-factory>com.ibm.faces.context.AjaxFacesContextFactory</faces-context-factory>
<render-kit-factory>com.ibm.faces.renderkit.AjaxRenderKitFactory</render-kit-factory>
</factory>
<lifecycle>
<phase-listener>com.ibm.faces.webapp.ValueResourcePhaseListener</phase-listener>
</lifecycle>
</faces-config>
And my spring-web.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="sessionBean" class="sanitycheck.SessionBean" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="pc_SanityCheckProjectView" class="pagecode.SanityCheckProjectView" scope="request" init-method="init">
<aop:scoped-proxy/>
<property name="sessionBean" ref="sessionBean"/>
</bean>
</beans>
I can provide my other files if necessary, just let me know. Thanks!
Edit: Added aop:scoped-proxy to the Spring beans.
Edit: Adding portlet.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" id="com.ibm.faces.portlet.FacesPortlet.3a22ca3014">
<portlet>
<portlet-name>SanityCheckProject</portlet-name>
<display-name xml:lang="en">SanityCheckProject</display-name>
<display-name>SanityCheckProject</display-name>
<portlet-class>com.ibm.faces.portlet.FacesPortlet</portlet-class>
<init-param>
<name>com.ibm.faces.portlet.page.view</name>
<value>/SanityCheckProjectView.jsp</value>
</init-param>
<init-param>
<name>whichOne</name>
<value>Portlet1</value>
</init-param>
<init-param>
<name>wps.markup</name>
<value>html</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<resource-bundle>com.ibm.sanitycheckproject.nl.SanityCheckProjectPortletResource</resource-bundle>
<portlet-info>
<title>SanityCheckProject</title>
<short-title>SanityCheckProject</short-title>
<keywords>SanityCheckProject</keywords>
</portlet-info>
</portlet>
<portlet>
<portlet-name>SanityCheckPortlet2</portlet-name>
<display-name xml:lang="en">SanityCheckPortlet2</display-name>
<display-name>SanityCheckPortlet2</display-name>
<portlet-class>com.ibm.faces.portlet.FacesPortlet</portlet-class>
<init-param>
<name>com.ibm.faces.portlet.page.view</name>
<value>/SanityCheckProjectView.jsp</value>
</init-param>
<init-param>
<name>whichOne</name>
<value>Portlet2</value>
</init-param>
<init-param>
<name>wps.markup</name>
<value>html</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<resource-bundle>com.ibm.sanitycheckproject.nl.SanityCheckPortlet2PortletResource</resource-bundle>
<portlet-info>
<title>SanityCheckPortlet2</title>
<short-title>SanityCheckPortlet2</short-title>
<keywords>SanityCheckPortlet2</keywords>
</portlet-info>
</portlet>
<default-namespace>http://SanityCheckProject/</default-namespace>
</portlet-app>
In Spring XML config, you must use the <aop:scoped-proxy/> tag.
http://static.springsource.org/spring/docs/3.0.x/reference/beans.html#beans-factory-scopes-other-injection
<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<!-- this next element effects the proxying of the surrounding bean -->
<aop:scoped-proxy/>
</bean>
I don't know if this was the best or even a very good solution, but what I eventually did was create two custom portlet scopes, one for request scope, and one for session scope. Essentially what my custom scopes do is that they prefix the name of the object requested with the portlet's namespace, which seems to keep everything separated.
Here is the code I used for my scopes:
Request Scope:
import javax.faces.context.FacesContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.web.context.request.RequestScope;
import com.ibm.faces.portlet.httpbridge.ActionResponseWrapper;
import javax.portlet.RenderResponse;
import javax.portlet.filter.RenderResponseWrapper;
public class PortletRequestScope extends RequestScope {
#Override
public Object get(String name, ObjectFactory objectFactory) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else {
writeError(response);
}
return super.get(name, objectFactory);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else {
writeError(response);
}
super.registerDestructionCallback(name, callback);
}
#Override
public Object remove(String name) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else {
writeError(response);
}
return super.remove(name);
}
protected void writeError(Object response) {
System.err.println("Error in PortletRequestScope");
System.err.println("Response is unrecognized class: " + response.getClass().getCanonicalName());
System.err.println("Please modify code to handle class");
}
}
Session Scope:
import javax.faces.context.FacesContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.web.context.request.SessionScope;
import com.ibm.faces.portlet.httpbridge.ActionResponseWrapper;
import javax.portlet.RenderResponse;
import javax.portlet.filter.RenderResponseWrapper;
public class PortletSessionScope extends SessionScope {
#Override
public Object get(String name, ObjectFactory objectFactory) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else {
writeError(response);
}
return super.get(name, objectFactory);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else {
writeError(response);
}
super.registerDestructionCallback(name, callback);
}
#Override
public Object remove(String name) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else {
writeError(response);
}
return super.remove(name);
}
protected void writeError(Object response) {
System.err.println("Error in PortletSessionScope");
System.err.println("Response is unrecognized class: " + response.getClass().getCanonicalName());
System.err.println("Please modify code to handle class");
}
}
Then, in my spring-web.xml, I defined my custom scopes:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="portletRequestScope">
<bean class="com.test.scope.PortletRequestScope"/>
</entry>
<entry key="portletSessionScope">
<bean class="com.test.portlet.scope.PortletSessionScope"/>
</entry>
</map>
</property>
</bean>
And when I defined my actual Spring beans, I used my custom scopes instead of the regular scope -- for example:
<bean id="sessionBean" class="com.test.managedbeans.SessionBean"
scope="portletSessionScope" lazy-init="true"/>
At the very least, doing this seemed to work in my specific situation of JSF + Spring on WebSphere Portal, and hopefully this will be useful to someone else.
Related
I I am very new to spring boot and only have been working with it for a couple of days, so I am also very confused about this project (which is not my own). I am supposed to write tests with MockMvc for the rest controller, however each test with MockMvc only returns the status code 200 although it should be a 404.
Here is one of the tests:
#WebMvcTest(ObjectController.class)
#SpringJUnitConfig(TestConfig.class)
public class MvcTest {
#Autowired
MockMvc mockMvc;
#Test
public void shouldReturn404() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/obj/123"))
.andExpect(MockMvcResultMatchers.status().is(HttpStatus.NOT_FOUND.value()));
}
}
This is my rest controller I would like to test.
#RestController
#RequestMapping("obj")
public class ObjectController {
#Autowired
MyClass myClass;
#GetMapping()
public List<MyObject> findAll() {
List<MyObject> objList;
objList = myClass.getObjects();
return objList;
}
#GetMapping("/{id}")
public MyObject findById(#PathVariable String id) {
for (MyObject obj : myClass.getObjects()) {
if (obj.getId().equals(id))
return obj;
}
return null;
}
}
Then there is this class:
public class MyClass {
private List<MyObject> objList = new ArrayList<MyObject>();
public MyObject addObject(MyObject obj) {
objList.add(obj);
return obj;
}
public void setObjects(List<MyObject> objList) {
this.objList = objList;
}
public synchronized List<MyObject> getObjects() {
return objList;
}
}
There is an xml file that belongs to the class looking like this which can be found in the resource folder:
<?xml version='1.0' encoding='ISO-8859-1'?>
<beans xmlns='http://www.springframework.org/schema/beans'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:context='http://www.springframework.org/schema/context'
xsi:schemaLocation='http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd'>
<bean id='MyClass' class='it.is.some.path.MyClass'>
<property name='objList'>
<list>
<ref bean='[R01] Object1' />
</list>
</property>
</bean>
</beans>
The referenced beans can be found in separate files under resources too, in a sub folder called 'objList' and look like this:
<?xml version='1.0' encoding='ISO-8859-1'?>
<beans xmlns='http://www.springframework.org/schema/beans'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:context='http://www.springframework.org/schema/context'
xsi:schemaLocation='http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd'>
<bean id='[R01] Object1' class='this.is.some.path.MyObject'>
<property name='id' value='123' />
</bean>
</beans>
The myclass.xml as well as the folder with all xmls of the objects are imported via #ImportResource in the Application class.
And then there is
public class MyObject {
public String id = RandomStringUtils.randomAlphanumeric(5);
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
am sorry for the lengthy explanation but I have been spending two days now trying to figure out how to get MockMvc running and I feel like I am completely stuck. I would be very, very grateful if anybody could help out.
I assume 200 is the correct response as you have a mapping for /obj/{id} where {id} is a placeholder for any path variable like 123 in /obj/123.
Even though your controller returns null from a code perspective, Spring won't map this automatically to a 404 not found if this is what you would expect.
So you can literally try it with every path variable, you'll always get HTTP 200 because there wasn't any exception and the DisptacherServlet could properly route the HTTP request to a controller mapping. HTTP 200 is the default response code if you don't specify anything.
If you want your controller endpoint to return 404 when your MyObject is null, you have to be more explicit about this, e.g.:
#GetMapping("/{id}")
public ResponseEntity<MyObject> findById(#PathVariable String id) {
for (MyObject obj : myClass.getObjects()) {
if (obj.getId().equals(id))
return ResponseEntity.ok(obj);
}
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
I am quite new to Java EE and Arquillian. I am getting problems if I want to deploy my simple project into the embedded glassfish 4.1.1 server. I can succesfully pass the deployment, but I can not access the methods that i implemented. I am getting error code 500, while i expect 200.
my deployment:
public class AuthTest
{
#Deployment(testable = false)
public static WebArchive createDeployment()
{
System.out.println("\nin createDeployment");
// File[] libs = Maven.resolver().loadPomFromFile("pom.xml").importRuntimeAndTestDependencies().asFile();
WebArchive archive = ShrinkWrap.create(WebArchive.class, "ArquillianHepInterface.war")
.addClasses(AuthEndpoint.class)
.addPackage(Database.class.getPackage())
.addPackage(DeviceAccessDBEntry.class.getPackage())
.addPackage(WebsiteAccessDBEntry.class.getPackage())
.addPackage(DeviceInfo.class.getPackage())
.addPackage(LoginInfo.class.getPackage())
.addPackage(ResponseBoolean.class.getPackage())
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsWebInfResource(new File("src/main/webapp", "WEB-INF/web.xml"))
.addAsWebInfResource(new File("src/main/webapp", "WEB-INF/glassfish-web.xml"))
.addAsResource("META-INF/persistence.xml")
// .addAsLibraries(libs)
;
System.out.println("\nende createDeployment");
return archive;
}
#ArquillianResource
protected URL contextPath;
#Test
public void registerUUIDTest()
{
DeviceInfo newUser = new DeviceInfo();
newUser.setDate("2014-12-12");
newUser.setUuid("xyz");
Response i = expect().statusCode(200).when().given().
contentType("application/json").body(newUser)
.post(this.contextPath + "AUTHENTICATION/CREATEMOBILELOGIN")
;
}
my AuthEndpoint class:
#Path("/AUTHENTICATION")
public class AuthEndpoint
{
#Inject
private DatabaseInterface database;
#POST
#Produces({ "application/json" })
#Consumes({ "application/json" })
#Path("CREATEMOBILELOGIN")
public LoginInfo createMobileLogin(DeviceInfo info)
{
System.out.println("RICHTIGES FILE");
System.err.println();
LoginInfo response = null;
if (info != null && info.getUuid() != null)
{
String password = this.database.registerDevice(info.getUuid());
if (password != null)
{
response = new LoginInfo();
response.setPw(password);
response.setUuid(info.getUuid());
}
}
return response;
}
}
I am getting:
SEVERE: WebModule[/ArquillianHepInterface]StandardWrapper.Throwable
Why is that happening? I do not understand where my problem is...
I supposed that I have problems while I am using glassfish 4.1.1...? I want simply to call the Methods in the AuthEndpoint class throught the Arquillian TestClass to verify that a single have been created...
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Arquillian Rest Demo</display-name>
<servlet>
<display-name>JAX-RS REST Servlet</display-name>
<servlet-name>REST-Servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>REST-Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
glassfish-web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
<context-root>/ArquillianHepInterface</context-root>
<class-loader delegate="true" />
</glassfish-web-app>
My beans.xml file is empty.
The methods that AuthEndpoint uses are in another maven project that I have compiled and added in the maven dependencies...
I would appreciate any help...
I am trying to create a multi user chat environment using the base example from https://github.com/rstoyanchev/spring-mvc-chat
I am not able to find what exactly is wrong with my code. The request is not polling it just keeps on firing request continuously to the server.
Following are my code snipets
web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
applicationContext.xml
<mvc:annotation-driven>
<mvc:async-support default-timeout="30000" />
<mvc:message-converters register-defaults="false">
<bean id="jacksonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
Controller
#Controller
#RequestMapping("/chat")
public class ChatController
{
#RequestMapping("messages")
#ResponseBody
public DeferredResult<List<String>> getMessages()
{
final DeferredResult<List<String>> deferredResult = new DeferredResult<List<String>>(30000l, Collections.emptyList());
this.chatRequests.put(deferredResult, 0);
deferredResult.onCompletion(new Runnable()
{
#Override
public void run()
{
chatRequests.remove(deferredResult);
}
});
List<String> messages = new ArrayList<String>();
messages.add("first");
if (!messages.isEmpty())
{
deferredResult.setResult(messages);
}
return deferredResult;
}
}
javascript
function chatModel(){
var started = false;
var that = this;
that.activePollingXhr = ko.observable(null);
function pollForMessages() {
that.activePollingXhr($.ajax({
url : contextPath + '/chat/messages',
type : "GET",
cache : false,
success : function(messages) {
console.log(messages);
},
error : function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
console.log('Unable to Connect');
}
},
complete : pollForMessages
}));
}
Got the issue.
if we set results
deferredResult.setResult();
it dont wait so goes into recursive calling.
I'm trying to use HttpInvokerServiceExporter + HttpInvokerProxyFactoryBean, but whatever I do I get an exception:
org.springframework.remoting.RemoteAccessException: Could not access HTTP invoker remote service at [http://localhost:9999/testcaseapp/testcaseservice]; nested exception is java.io.IOException: Did not receive successful HTTP response: status code = 404, status message = [Not Found]
For the simplicity, I've created a test case.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class RemoteTest {
private static final Logger logger = LoggerFactory.getLogger("TestsLogger");
static interface TestCaseService {
public Integer add(Integer arg1, Integer arg2);
}
static class TestCaseServiceImpl implements TestCaseService {
public Integer add(Integer arg1, Integer arg2) {
return (arg1 != null ? arg1.intValue() : 0) + (arg2 != null ? arg2.intValue() : 0);
}
}
#Configuration
static class Config {
#Bean
public HttpInvokerServiceExporter httpInvokerServiceExporter() {
HttpInvokerServiceExporter httpInvokerServiceExporter = new HttpInvokerServiceExporter();
httpInvokerServiceExporter.setService(new TestCaseServiceImpl());
httpInvokerServiceExporter.setServiceInterface(TestCaseService.class);
return httpInvokerServiceExporter;
}
#Bean
public HttpInvokerProxyFactoryBean httpInvokerProxyFactoryBean() {
HttpInvokerProxyFactoryBean httpInvokerProxyFactoryBean = new HttpInvokerProxyFactoryBean();
httpInvokerProxyFactoryBean.setServiceInterface(TestCaseService.class);
httpInvokerProxyFactoryBean.setServiceUrl("http://localhost:9999/testcaseapp/testcaseservice");
httpInvokerProxyFactoryBean.afterPropertiesSet();
return httpInvokerProxyFactoryBean;
}
}
#Autowired
private TestCaseService[] testCaseServices;
private static Server server;
#BeforeClass
public static void setUp() {
try {
server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(9999);
server.addConnector(connector);
//
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/testcaseapp");
webAppContext.setWar("src/test/java/" + RemotingTest.class.getPackage().getName().replace('.', '/'));
server.setHandler(webAppContext);
//
server.start();
} catch (Exception ex) {
logger.info("Could not permorm the set up: {}", ex.toString());
}
}
#AfterClass
public static void destroy() {
try {
server.stop();
} catch (Exception e) {
}
}
#Test
public void addTest() {
for (TestCaseService testCaseService : testCaseServices) {
Integer sum = testCaseService.add(10, 5);
Assert.assertNotNull(sum);
Assert.assertEquals(15, sum.intValue());
}
}
}
I've also tried to create a TestCaseService bean
#Bean public TestCaseService testCaseService() ...
and provide it as a httpInvokerServiceExporter argument
#Bean public HttpInvokerServiceExporter httpInvokerServiceExporter(TestCaseService testCaseService)
...
httpInvokerServiceExporter.setService(testCaseService);
but the result is still the same.
What am I doing wrong? Thanks!
I think the problem is that the Servlet is not accesible.
SERVER SIDE
Make sure you have in your WEB-INF/web.xml (on the app that is exposing the methods -SERVER-) this code:
<web-app>
...
<servlet>
<servlet-name>remoting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
...
</web-app>
Here, the remote methods are served under "services", that is, for calling the method, the URL should be:
http://localhost:8080/sample/services/list
And you have to define this Servlet as accesible, by creating a bean (in my case under WEB-INF/remoting-servlet.xml):
<bean name="/list" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="myObjectQueryService" />
<property name="serviceInterface" value="com.kategor.myapp.sample.service.ObjectQueryService" />
</bean>
CLIENT SIDE
If your using Spring under the client (not as in your example), you must define a bean for accessing the remote resources, defining some beans (one for each public resource):
In this case, it would be:
<bean id="listService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="http://localhost:8080/sample/services/list" />
<property name="serviceInterface" value="com.kategor.myapp.sample.service.ObjectQueryService" />
</bean>
In your example is right.
This way, calling the Service "listService", you would have all the methods available in the class com.kategor.myapp.sample.service.ObjectQueryService
#Controller
public class HomeController {
// This is the remote service definition
#Autowired
private ObjectQueryService<MyObject, Long> objectQueryService;
/* .... */
/**
* List all Objects retrieved through Web Service from a remote Server
*/
#RequestMapping(value = "listRemoteWS", method = RequestMethod.GET)
public String listRemoteWS(Locale locale, Model model) {
StringBuilder result = new StringBuilder();
try {
// The Remote Service is called
List objs = objectQueryService.findAll(0, 10);
result.append(objs.size() + " objs found");
for (MyObject o : objs) {
result.append("<br>* ").append(o.getId()).append(" = ").append(o.getName());
}
} catch (Exception e) {
result.append("No objs have been found");
e.printStackTrace();
}
model.addAttribute("result", result);
return "index";
}
}
So I think the problem comes from the URL: maybe the service is not visible or this is not the correct path to it.
For more information, check this links (the first is really useful):
https://github.com/JamesEarlDouglas/barebones-spring-mvc/tree/master/reference/spring-remoting
http://www.ibm.com/developerworks/web/library/wa-spring3webserv/index.html
For me the problem was tomcat picked up two versions of the same applications. This raised the above error on running the client from STS in debug mode.
So solution is to clean up all the expanded webapp folders in tomcat for the application. Then redeploy the application.
I am new in Spring Integeration.I have one requirement Using spring integeration
read a txt file (from Source folder)
do some validation
if validation is success -write into sucess file (in sucess folder)
If the validation is fail -write into failure file (in error folder)
if the file format is incorrect means I have to move that file into error folder(Ex excepted columns is 2 but in my file contain columns is 1)
My config file is like this
<?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:si="http://www.springframework.org/schema/integration"
xmlns:file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd">
<bean id="checkCSVReader"
class="com.check.wrapper">
<property name="pzMapXML" value="classpath:sampleFileFormat.xml" />
</bean>
<bean id="checkTrasnFomer"
class="com.check.checkTransfomer">
<property name="wrapper" ref="checkCSVReader" />
</bean>
<bean id="fileErrorProcessor"
class="com.check.ErrorChannelWriter">
</bean>
<bean id="listToStringTrans"
class="com.check.ListToStringTransfomer"></bean>
<bean id="validation"
class="com.check.Validation"/>
<file:inbound-channel-adapter directory="file://D:\check\soruce" prevent-duplicates="false"
auto-create-directory="true" channel="readChannel" >
<si:poller id="Poller">
<si:interval-trigger interval="10000" />
</si:poller>
</file:inbound-channel-adapter>
<si:channel id="readChannel" />
<si:chain input-channel="readChannel" output-channel="processChannel">
<si:header-enricher error-channel="errorFile" />
<file:file-to-string-transformer />
<si:transformer ref="checkTrasnFomer" method="transform" />
<si:service-activator ref="validation"
method="validate" />
</si:chain>
<si:channel id="processChannel" />
<si:transformer ref="listToStringTrans" method="transformList"
input-channel="processChannel" output-channel="finalOut" />
<si:channel id="finalOut" />
<file:outbound-channel-adapter id="checkSuccFileOutBound"
auto-create-directory="true" delete-source-files="true"
directory="file://D:\check\success" channel="finalOut">
</file:outbound-channel-adapter>
<si:channel id="errorFile" />
<si:transformer ref="fileErrorProcessor"
input-channel="errorFile" output-channel="errorChannel" method="transformError" />
<file:outbound-channel-adapter id="errorChannel"
directory="file://D:\check\error" delete-source-files="true"
/>
<si:channel id="checkFileErr" />
</beans>
my checkFlatPackCVSParserWrapper class is
public class checkFlatPackCVSParserWrapper {
private static final Log LOG = LogFactory.getLog("checkFlatPackCVSParserWrapper");
private Resource pzMapXML;
private char delimiter = ',';
private char qualifier = '"';
private boolean ignoreFirstRecord = false;
public Resource getPzMapXML() {
return pzMapXML;
}
public void setPzMapXML(Resource pzMapXML) {
this.pzMapXML = pzMapXML;
}
public char getDelimiter() {
return delimiter;
}
public void setDelimiter(char delimiter) {
this.delimiter = delimiter;
}
public char getQualifier() {
return qualifier;
}
public void setQualifier(char qualifier) {
this.qualifier = qualifier;
}
public boolean isIgnoreFirstRecord() {
return ignoreFirstRecord;
}
public void setIgnoreFirstRecord(boolean ignoreFirstRecord) {
this.ignoreFirstRecord = ignoreFirstRecord;
}
public Parser getParser(String csv) {
if(LOG.isDebugEnabled())
LOG.debug("getParser: " + csv);
Parser result = null;
try {
result = DefaultParserFactory.getInstance().newDelimitedParser(
pzMapXML.getInputStream(), //xml column mapping
new ByteArrayInputStream(csv.getBytes()), //txt file to parse
delimiter, //delimiter
qualifier, //text qualfier
ignoreFirstRecord);
}catch (Exception e) {
if(LOG.isDebugEnabled())
LOG.debug("Unable to read file: " + e );
throw new RuntimeException("File Parse exception");
}
return result;
}
}
sampleFileFormat.xml is
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE PZMAP SYSTEM "flatpack.dtd" >
<PZMAP>
<COLUMN name="FIRSTNAME" />
<COLUMN name="LASTNAME" />
</PZMAP>
and checkTransfomer is
public class checkTransfomer {
private static final Log LOG = LogFactory.getLog(checkTransfomer.class);
private CheckFlatPackCVSParserWrapper wrapper;
public String transform(String csv) {
Parser parser = wrapper.getParser(csv);
if(LOG.isDebugEnabled()) {
LOG.debug("Parser is: " + parser);
}
DataSet ds = parser.parse();
ArrayList<Check> list = new ArrayList<Check>();
while(ds.next()) {
Check check= new Check();
check.setFirstName(ds.getString("FIRSTNAME"));
check.setLastName(ds.getString("LASTNAME"));
if(LOG.isDebugEnabled()) {
LOG.debug("Bean value is: " + bean);
}
list.add(bean);
}
if(LOG.isDebugEnabled()) {
LOG.debug("Records fetched is: " + list.size());
}
return list.toString();
}
public CheckFlatPackCVSParserWrapper getWrapper() {
return wrapper;
}
public void setWrapper(CheckFlatPackCVSParserWrapper wrapper) {
this.wrapper = wrapper;
}
And my ErrorChannelWriter is
public class ErrorChannelWriter {
public static final Log LOG = LogFactory.getLog(ErrorChannelWriter.class);
public Message<?> transformError(ErrorMessage errorMessage) {
if (LOG.isDebugEnabled()) {
LOG.debug("Transforming errorMessage is: " + errorMessage);
}
return ((MessagingException) errorMessage.getPayload())
.getFailedMessage();
}
}
and my validagtion class is
com.check.Validation
public class Validation
{
void validation(CheckCheck)
{
if(Check.getFirstName().equals("maya"))
{
throw new RuntimeException("Name Already exist");
}
}
}
and my ListToStringTransfomer is
public class ListToStringTransfomer {
private static final Log LOG=LogFactory.getLog(ListToStringTransfomer.class);
public String transformList(List<IssueAppBean> list) {
return list.toString();
}
}
and my file containing one fields instead of two fields
> maya
here my file format is wrong, so record is moving to error folder.but there is no error message. how can i add error message(TOO FEW COLUMNS WANTED: 2 GOT: 1) when my file format is incorrect.
my requirement is in my error file should contaion
maya -TOO FEW COLUMNS WANTED: 2 GOT: 1 or(Any error message )
please give me any solution
I don't think that you should go through the error channel to solve this requirement. The main reason for this is that invalid input in this case is an expected scenario. The errorChannel is the channel that Spring Integration sends messages to if an unexpected exception happens in an endpoint.
If you add some header to the message if a validation failed, you can route based on that header and also record the failure message there. You can then send your error message to a log file or whatever on your own.