Tag Archives: Spring 3

Dynamic for with Spring MVC using a HashMap

Sometime you need to dynamically generate a form without knowing how many fields it will be required (i.e. when your form is driven by a configuration or by some properties). The problem is to draw the form and, return the values to the Controller, and recognize the couples Field name / Field value after the submit..

You can easily get solve this problem just adding HashMap which will hold the key-value pair data to the DataModel.

Supposing your configuration says: you have to draw two fields and these are the name, you UI will be something like*:

<c:forEach items="${newRequest.fields}" var="field">
	<f:input type="text" path="rawFields['${field.field_id}']" class="form-control validate[groupRequired[mandatoryField]]" /> (R)
</c:forEach>

When you submit the form, the values and the key for the dynamic fields will be filled.

* newRequest is the DataModel you are passing and fields is the list of Fields that user will fill with data, like that:

public class Request {
 
	/** Request type */
	private int templateRequest;
 
	// ***** Input field ***** 
	List<RequestField> fields = new ArrayList<RequestField>();
 
	private HashMap<String, Object> rawFields = new HashMap<String, Object>();
 
	[Setters and getters]
 
}
Tagged , , ,

dataSource: The name of the property, following JavaBean naming conventions.

Suppose you defined TemplateDao Spring bean in this way:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">	
	<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>	
	<property name="url"><value>jdbc:mysql://${mysql.hostname}:${mysql.port}/${mysql.db}</value></property>
	<property name="username"><value>${mysql.user}</value></property>
	<property name="password"><value>${mysql.password}</value></property>
</bean>	
 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean> 
 
<bean id="TemplateRequestDao" class="com.afm.admin.dao.mysql.TemplateDaoMySql">
	<property name="dataSource" ref="dataSource" />
</bean>

and you get this error message: The name of the property, following JavaBean naming conventions. The reason is probably you forgot to extend your implementation class:

public class TemplateDaoMySql extends JdbcDaoSupport implements TemplateDao {
 
	@Override
	public List<TemplateRequest> getTemplateRequest(UserProfile userProfile) {
		// TODO Auto-generated method stub
		return null;
	}
 
}

Time lost to fix this issue: more or less 1 hour. Image the how many I was frustrated when I discovered what was the problem.

Tagged , ,

Get a list of Object from jdbcTemplate()

Sometime happens get a list of object from database and, to do that without call a RowMapper, use this:

List<Object> strings = (List<Object>) jdbcTemplate.queryForList(query, Object.class);

This is a sample to get a list of String:

List<String> strings = (List<String>) jdbcTemplate.queryForList(query, String.class);
Tagged

Spring-data: Cannot use a complex object as a key value

I was trying to figured out how to solve this issue. Basically I’m saving a user profile bean that contains multiple occurrences of other beans inside him. Something like date:

public class UserProfile {
 
	List<Class1> classes1;
	List<Class2> classes2;
 
	Integer int1;
	Map<String, Class3> classes3;
 
}

I was working fine until I changed a getter adding some business functionalities and surrounding all lines with a try / catch. Of course, I putted the exception message on the log using adding this definition inside the bean:

protected final Log logger = LogFactory.getLog(getClass());

Good point for me, everything worked fine until I updated the object in Mongo. Holy crap, I got the “Cannot use a complex object as a key value” exception and I spent two hours trying to change the beans (of course I did a lot of other changes after the fatal adding of business logic inside my bean). Exhausted, I contacted my colleague Max (@maxfarnea) and he told me, “when I got this error, I removed the log inside a bean”. Ipso facto, I removed the log entry from my bean, added a throws exception instead of a try / catch and tested! Well done @maxfarnea, it works!

I learned two lesson today: one, don’t put log into beans that need to be stored using spring-data and, two, if you are in pain, ask, talk, don’t be silly, don’t waste your time!!! Stackoverflow.com is a good point to start (and my blog either, of course) but never is more helpful than a quick chat with a colleague!

Tagged , ,

No suitable driver found for jdbc:mysql://localhost:3306/schema

If the exception “Could not get JDBC Connection; nested exception is java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/schema” is raised probably you forgot to add the

<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>

property to your dataSource bean:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">		
	<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
	<property name="url"><value>jdbc:mysql://172.16.0.11:3306/test_vale</value></property>
	<property name="username"><value>root</value></property>
	<property name="password"><value>password</value></property>
</bean>
Tagged , , ,

JSP Error : Attribute qualified names must be unique within an element

Today I updated an old Spring MVC application to Apache Tomcat 7 and some other newer jars and, when I started it, I get this error:

SEVERE: Servlet.service() for servlet [mvc-dispatcher] in context with path [/CG] threw exception [/WEB-INF/view/main.jsp (line: 2, column: 0) /WEB-INF/view/include.jsp (line: 3, column: 75) Attribute qualified names must be unique within an element] with root cause
org.apache.jasper.JasperException: /WEB-INF/view/main.jsp (line: 2, column: 0) /WEB-INF/view/include.jsp (line: 3, column: 75) Attribute qualified names must be unique within an element

After some search i find out that using single attribute multiple time in a single tag throw this error(it works with no problem in previous version!!!)

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>

I removed the duplicated “prefix” tag and everything worked fine! (…ehm… everything? Of course not… but this exception has been fixed)

Tagged ,

How to download PDF file from url on MVC controller

To download a remote file (like a PDF) redirecting to response output, use these instructions to update a your Spring MVC controller:

@RequestMapping(value="/viewAttach", method = RequestMethod.GET)
public ModelAndView viewAttach(@RequestParam(value="article_id", required = true) String article_ref, HttpSession session, HttpServletResponse response) 
{
	/* *** Remember to check if Session still valid  *** */
	try {
 
		URL url = new URL(remoteURL);        	
		response.setHeader("Content-disposition", "attachment;filename=" + filename);
 
		//Set the mime type for the response
		response.setContentType("application/pdf");
 
		// URLConnection connection = url.openConnection();
		InputStream is = url.openStream();
 
		BufferedOutputStream outs = new BufferedOutputStream(response.getOutputStream());
		int len;
		byte[] buf = new byte[1024];
		while ( (len = is.read(buf)) &gt; 0 ) {
			outs.write(buf, 0, len);
		}
		outs.close();
 
	} catch (MalformedURLException e) {
		logger.error("Error ModelAndView.viewMain - MalformedURLException : " + e.toString() + " -- " + e.getStackTrace()[0].toString());
		return null;
	} catch (IOException e) {
		logger.error("Error ModelAndView.viewMain - IOException : " + e.toString() + " -- " + e.getStackTrace()[0].toString());
		return null;
	}
 
	return null;
 
}
Tagged ,

Route a message to MongoDB

The requirement is very simple. Route an XML message from rabbitMQ to MongoDB. MongoDB BSON as the data storage and network transfer format for “documents”. BSON is a binary-encoded serialization of JSON-like documents. So, the source message is in a XML format, after getting it from rabbitMQ is necessary to translate into a JSON format compatible with MongoDB.

To translate a message from XML to JSON is possible to use Marshalling function available in Camel extension XmlJson. The pom.xml file needs this new entry:

<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-xmljson</artifactId>
	<version>${org.camel.version}</version>
</dependency>

Of course, to send communicate with MongoDB another entry has to be added to pom.xml:

<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-mongodb</artifactId>
	<version>${org.camel.version}</version>
</dependency>

Now, is possible to change the route on camel ApplicationContext file:

<!-- Camel route -->    
<camelContext xmlns="http://camel.apache.org/schema/spring">
	<dataFormats>
		<xmljson id="xmljson"/>
	</dataFormats>
	<route> 
		<from uri="spring-amqp:TPDirect:TPQueue:TPRouting?type=direct&amp;autodelete=true&amp;durable=true" />
		<marshal ref="xmljson"/>
		<log message="From XML to Json: DONE!" />
		<convertBodyTo type="java.lang.String"/>
		<to uri="mongodb:myDb?database=flights&amp;collection=tickets&amp;operation=save" />
	</route>                                 
</camelContext>
 
 
<!-- Mongo DB -->
<bean id="myDb" class="com.mongodb.Mongo">
	<constructor-arg index="0" value="localhost"/>
</bean>

Bean myDB contains the information to reach MongoDB. It’s also possible to define it using the full url:

<bean id="myDb" class="com.mongodb.Mongo"> 
    <constructor-arg index="0"> 
        <bean class="com.mongodb.MongoURI"> 
            <constructor-arg index="0" value="mongodb://username:password@host:port/db" /> 
        </bean> 
    </constructor-arg> 
</bean>

To avoid this exception:

Caused by: No type converter available to convert from type: byte[] to the required type: org.apache.camel.component.mongodb.converters.MongoDbBasicConverters with value

The JSON translated message has to be converted into String.

That’s all!

Tagged , , ,

Camel and RabbitMQ : Finally, how to!

Define a RabbitMQ broker endpoint in Camel is possible with the Bluelock camel-spring-amqp (https://github.com/Bluelock/camel-spring-amqp) library. It’s an Apache Camel component that allows to natively communicate with a RabbitMQ broker and it’s implemented using Spring’s AMQP.

For first, with Eclipse IDE create a new Maven Project with Artifact ID camel-arthetype-spring. This allows using Spring DSL to configure Camel route and execute the run:camel goal of Camel Mavel Pluing (Camel Maven Plugin) in a forked JVM from Maven.

To resolve the dependencies, these entries are mandatory:

<dependency>
	<groupId>com.bluelock</groupId>
	<artifactId>camel-spring-amqp</artifactId>
	<version>1.2</version>
</dependency>
 
<!-- Camel dependencies -->
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-test</artifactId>
	<version>${org.camel.version}</version>          
</dependency>
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-spring</artifactId>
	<version>${org.camel.version}</version>        
	<exclusions>
		<exclusion>
			<artifactId>spring-tx</artifactId>
			<groupId>org.springframework</groupId>
		</exclusion>
		<exclusion>
			<artifactId>spring-context</artifactId>
			<groupId>org.springframework</groupId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-xstream</artifactId>
	<version>${org.camel.version}</version>            
</dependency>

At this point edit the camel-context.xml available on src/main/resource/META-INF/spring folder:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Configures the Camel Context-->
 
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       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
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
       http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd">
 
	<camelContext xmlns="http://camel.apache.org/schema/spring">
		<route>	
			<from uri="spring-amqp:KipcastDirect:KipcastQueue:KipcastRouting?type=direct&amp;autodelete=true&amp;durable=true" />
			<log message="Message available on a RabbitMQ Queue" />			
			<process ref="processorTest" />
		</route>
	</camelContext>
 
	<rabbit:connection-factory id="amqpConnectionFactory" />
	<rabbit:template id="amqpTemplate" connection-factory="amqpConnectionFactory" message-converter="messageConverter" exchange="KipcastBean" />
	<rabbit:admin connection-factory="amqpConnectionFactory"/>
 
	<bean id="amqpConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
	    <property name="host" value="10.211.55.20"/>
	    <property name="port" value="5672"/>
	    <property name="username" value="guest"/>
	    <property name="password" value="guest"/>
	    <property name="virtualHost" value="/"/>
	</bean>
 
    <bean id="jsonMessageConverter" class="amqp.spring.converter.XStreamConverter"/>
    <bean id="textMessageConverter" class="amqp.spring.converter.StringConverter"/>
    <bean id="messageConverter" class="amqp.spring.converter.ContentTypeConverterFactory">
        <property name="converters">
            <map>
                <entry key="application/json" value-ref="jsonMessageConverter"/>
                <entry key="application/xml" value-ref="textMessageConverter"/>
            </map>
        </property>
        <property name="fallbackConverter" ref="textMessageConverter"/>
    </bean>
 
</beans>

In this case, a route starting from a RabbitMQ queue to a system log and a listener on your queue that will be active until you terminate your maven came:run process has been created.

It’s very important to note that, on Spring XML & has to be quoted &amp;

The following Java code should be used send a message to the Exchange defined on Camel Route:

@Test
public void test() throws KeyManagementException, NoSuchAlgorithmException, URISyntaxException, IOException {
	ConnectionFactory factory = new ConnectionFactory();
	factory.setHost("10.211.55.20");
	factory.setPort(5672);
	factory.setVirtualHost("/");
	factory.setUsername("guest");
	factory.setPassword("guest");
	Connection connection = factory.newConnection();
	Channel channel = connection.createChannel();
 
	channel.exchangeDeclare("KipcastDirect", "direct", 
		   true, 	/* durable */
		   true, 	/* autodelete */
		   null); 	/* */
 
	byte[] messageBodyBytes = "Hello, world!".getBytes();
 
	AMQP.BasicProperties.Builder basic = new AMQP.BasicProperties.Builder();
	AMQP.BasicProperties minBasic = basic.build();
 
	minBasic = basic.priority(0).deliveryMode(1).build();
 
	channel.basicPublish("KipcastDirect", "KipcastRouting", minBasic, messageBodyBytes);
	System.out.println(" [x] Sent ");
 
	channel.close();
}

To test if listener works, for first run the queue listener and, after that, run the Junit class to send message. The output will be:

[pache.camel.spring.Main.main()] MainSupport                    INFO  Apache Camel 2.10.3 starting
[pache.camel.spring.Main.main()] SpringCamelContext             INFO  Apache Camel 2.10.3 (CamelContext: camel-1) is starting
[pache.camel.spring.Main.main()] ManagementStrategyFactory      INFO  JMX enabled.
[pache.camel.spring.Main.main()] DefaultTypeConverter           INFO  Loaded 177 type converters
[pache.camel.spring.Main.main()] SpringAMQPComponent            INFO  Found AMQP ConnectionFactory in registry for 10.211.55.20
[pache.camel.spring.Main.main()] SpringAMQPComponent            INFO  Found AMQP Template in registry
[pache.camel.spring.Main.main()] SpringAMQPComponent            INFO  Found AMQP Administrator in registry
[pache.camel.spring.Main.main()] SpringAMQPEndpoint             INFO  Creating endpoint for KipcastDirect:KipcastQueue:KipcastRouting
[pache.camel.spring.Main.main()] SpringAMQPConsumer             INFO  Declared exchange KipcastDirect
[pache.camel.spring.Main.main()] SpringAMQPConsumer             INFO  Declared queue KipcastQueue
[pache.camel.spring.Main.main()] SpringAMQPConsumer             INFO  Declaring binding KipcastRouting
[pache.camel.spring.Main.main()] SpringAMQPConsumer             INFO  Started AMQP Async Listeners for spring-amqp://KipcastDirect:KipcastQueue:KipcastRouting?autodelete=true&amp;durable=true&amp;type=direct
[pache.camel.spring.Main.main()] SpringCamelContext             INFO  Route: route1 started and consuming from: Endpoint[spring-amqp://KipcastDirect:KipcastQueue:KipcastRouting?autodelete=true&amp;durable=true&amp;type=direct]
[pache.camel.spring.Main.main()] ultManagementLifecycleStrategy INFO  StatisticsLevel at All so enabling load performance statistics
[pache.camel.spring.Main.main()] SpringCamelContext             INFO  Total 1 routes, of which 1 is started.
[pache.camel.spring.Main.main()] SpringCamelContext             INFO  Apache Camel 2.10.3 (CamelContext: camel-1) started in 0.505 seconds
[     SimpleAsyncTaskExecutor-1] route1                         INFO  Message available on a RabbitMQ Queue

At this point, you can have fun with Camel and RabbitMQ!!!!!

NOTES:

1 – Please be careful: the URI (from and to) on Camel Spring DSL context and JUnit class must refer to same Exchange and Queue to prevent a reply-text=PRECONDITION_FAILED – parameters for queue ‘QUEUE’ in vhost ‘/’ not equivalen error or similar. To check the queues/exchanges configuration parameter using:

rabbitmqadmin -V / list queue
rabbitmqadmin -V test list exchanges

if you like this post, please click on the advertise :)

Tagged , ,

Put SQL result into a map

To extract two or more object (in this sample two Integer) from a DB and putting into a map, use this:

final Map<Integer,Integer> topic = new HashMap<Integer,Integer>();
 
getJdbcTemplate().query(sqlCommand, new Object[] {query_parameter}, new RowMapper<Object>(){
	public Object mapRow(ResultSet rs, int arg1) throws SQLException {
		topic.put(rs.getInt("integer1"), rs.getInt("integer2"));
		return null;
	}
});

To read all values:

Iterator<Map.Entry<Integer, Integer>> users = userScheduledToday.entrySet().iterator();
while ( users.hasNext() ) {
	Map.Entry<Integer, Integer> entry = users.next();
	logger.debug("Key: " + entry.getKey() + " - Value: " + entry.getValue());
}
Tagged , ,