I am new to Spring and Hibernate and I would like to create Restful Web services using these technologies.
I am getting 404 exception when calling the URL http://localhost:8080/RakeshMavenProject/book.
I have created controller,service,Dao classes and mentioned the configuration parameters in AppConfig.java
#Configuration
#EnableTransactionManagement
#ComponentScans(value = {#ComponentScan("com.example.ws.maven_web_service_project.service"),
#ComponentScan("com.example.ws.maven_web_service_project.dao")})
public class AppConfig {
#Bean
public LocalSessionFactoryBean sessionFactoryBean(){
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan(new String[] { "com.example.ws.maven_web_service_project.model" });
sessionFactoryBean.setHibernateProperties(hibernateProperties());
return sessionFactoryBean;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/hibernate_test");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
private Properties hibernateProperties(){
Properties props = new Properties();
props.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
props.put("hibernate.show_sql", "true");
props.put("hibernate.hbm2ddl.auto", "update");
// Setting C3P0 properties
props.put("hibernate.c3p0.min_size", 5);
props.put("hibernate.c3p0.max_size", 20);
props.put("hibernate.c3p0.acquire_increment",
1);
props.put("hibernate.c3p0.timeout", 1800);
props.put("hibernate.c3p0.max_statements", 150);
return props;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s){
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
The code of server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--><!-- Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
--><Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
<!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443
This connector uses the NIO implementation. The default
SSLImplementation will depend on the presence of the APR/native
library and the useOpenSSL attribute of the
AprLifecycleListener.
Either JSSE or OpenSSL style configuration may be used regardless of
the SSLImplementation selected. JSSE style configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
type="RSA" />
</SSLHostConfig>
</Connector>
-->
<!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
This connector uses the APR/native implementation which always uses
OpenSSL for TLS.
Either JSSE or OpenSSL style configuration may be used. OpenSSL style
configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="150" SSLEnabled="true" >
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
certificateFile="conf/localhost-rsa-cert.pem"
certificateChainFile="conf/localhost-rsa-chain.pem"
type="RSA" />
</SSLHostConfig>
</Connector>
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
<Engine defaultHost="localhost" name="Catalina">
<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
</Realm>
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t "%r" %s %b" prefix="localhost_access_log" suffix=".txt"/>
<Context docBase="RakeshMavenProject" path="/RakeshMavenProject" reloadable="true" source="org.eclipse.jst.jee.server:RakeshMavenProject"/></Host>
</Engine>
</Service>
</Server>
Update : I am mentioning the code of my controller class
#RestController
public class BookController {
#Autowired
private BookService bookService;
//#PostMapping("/book")
#RequestMapping(value="/book/", method= RequestMethod.POST,headers="Accept=application/json")
public #ResponseBody ResponseEntity<?> saveBook(#RequestBody Book book){
int bookId = bookService.saveBook(book);
return ResponseEntity.ok().body("Your Book with Id "+ bookId +" save successfully.");
}
// #GetMapping("/book/{id}")
#RequestMapping(value="/book/{id}", method= RequestMethod.GET,headers="Accept=application/json")
public #ResponseBody ResponseEntity<Book> getBook(#PathVariable("id") int bookId){
Book book = bookService.getBook(bookId);
return ResponseEntity.ok().body(book);
}
#RequestMapping(value="/book/", method= RequestMethod.GET,headers="Accept=application/json")
public #ResponseBody ResponseEntity<List<Book>> list() {
List<Book> books = bookService.listBooks();
return ResponseEntity.ok().body(books);
}
}
Please anyone help me to find the solution.
You need to specify mapping either at controller level #RestContoller('myController') or at each method level like
#RequestMapping(value="myController/book/", method= RequestMethod.POST,headers="Accept=application/json")
The your url will be http://localhost:8080/RakeshMavenProject/myController/book.
Your request mapping has a typo. try using
#RequestMapping(value="/book") or access your app using http://localhost:8080/RakeshMavenProject/book/
It should work fine if there are no further error.
Related
I'm working with ActiveMQ Artemis 2.17 and Spring Boot 2.5.7.
I'm publishing messages on topic and queue and consuming it. All is done through JMS.
All queues (anycast or multicast) are durables. My topic (multicast address) has 2 durable queues in order to have 2 separate consumers.
For my topic, the 2 consumers use durable and shared subscriptions (JMS 2.0).
All processing is transactional, managed through Atomikos transaction manager (I need it for a 2 phases commit with the database).
I have a problem with the redelivery policy and DLQ. When I throw an exception during the processing of the message, the redelivery policy applies correctly for the queue (anycast queue) and a DLQ is created with the message. However, for the topic (multicast queue), the redelivery policy does not apply and the message is not sent into a DLQ.
Here is my ActiveMQ Artemis broker configuration:
<?xml version='1.0'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<configuration xmlns="urn:activemq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
<core xmlns="urn:activemq:core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:activemq:core ">
<name>0.0.0.0</name>
<!-- Codec and key used to encode the passwords -->
<!-- TODO : set master-password configuration into the Java code -->
<password-codec>org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;key=UBNTd0dS9w6f8HDIyGW9
</password-codec>
<!-- Configure the persistence into a database (postgresql) -->
<persistence-enabled>true</persistence-enabled>
<store>
<database-store>
<bindings-table-name>BINDINGS_TABLE</bindings-table-name>
<message-table-name>MESSAGE_TABLE</message-table-name>
<page-store-table-name>PAGE_TABLE</page-store-table-name>
<large-message-table-name>LARGE_MESSAGES_TABLE</large-message-table-name>
<node-manager-store-table-name>NODE_MANAGER_TABLE</node-manager-store-table-name>
<jdbc-lock-renew-period>2000</jdbc-lock-renew-period>
<jdbc-lock-expiration>20000</jdbc-lock-expiration>
<jdbc-journal-sync-period>5</jdbc-journal-sync-period>
<!-- Configure a connection pool -->
<data-source-properties>
<data-source-property key="driverClassName" value="org.postgresql.Driver"/>
<data-source-property key="url" value="jdbc:postgresql://localhost/artemis"/>
<data-source-property key="username" value="postgres"/>
<data-source-property key="password" value="ENC(-3eddbe9664c85ec7ed63588b000486cb)"/>
<data-source-property key="poolPreparedStatements" value="true"/>
<data-source-property key="initialSize" value="2"/>
<data-source-property key="minIdle" value="1"/>
</data-source-properties>
</database-store>
</store>
<!-- Configure the addresses, queues and topics default behaviour -->
<!-- See: https://activemq.apache.org/components/artemis/documentation/latest/address-model.html -->
<address-settings>
<address-setting match="#">
<dead-letter-address>DLA</dead-letter-address>
<auto-create-dead-letter-resources>true</auto-create-dead-letter-resources>
<dead-letter-queue-prefix/>
<dead-letter-queue-suffix>.DLQ</dead-letter-queue-suffix>
<expiry-address>ExpiryQueue</expiry-address>
<auto-create-expiry-resources>false</auto-create-expiry-resources>
<expiry-queue-prefix/>
<expiry-queue-suffix>.EXP</expiry-queue-suffix>
<expiry-delay>-1</expiry-delay>
<max-delivery-attempts>5</max-delivery-attempts>
<redelivery-delay>250</redelivery-delay>
<redelivery-delay-multiplier>2.0</redelivery-delay-multiplier>
<redelivery-collision-avoidance-factor>0.5</redelivery-collision-avoidance-factor>
<max-redelivery-delay>10000</max-redelivery-delay>
<max-size-bytes>100000</max-size-bytes>
<page-size-bytes>20000</page-size-bytes>
<page-max-cache-size>5</page-max-cache-size>
<max-size-bytes-reject-threshold>-1</max-size-bytes-reject-threshold>
<address-full-policy>PAGE</address-full-policy>
<message-counter-history-day-limit>0</message-counter-history-day-limit>
<default-last-value-queue>false</default-last-value-queue>
<default-non-destructive>false</default-non-destructive>
<default-exclusive-queue>false</default-exclusive-queue>
<default-consumers-before-dispatch>0</default-consumers-before-dispatch>
<default-delay-before-dispatch>-1</default-delay-before-dispatch>
<redistribution-delay>0</redistribution-delay>
<send-to-dla-on-no-route>true</send-to-dla-on-no-route>
<slow-consumer-threshold>-1</slow-consumer-threshold>
<slow-consumer-policy>NOTIFY</slow-consumer-policy>
<slow-consumer-check-period>5</slow-consumer-check-period>
<!-- We disable the automatic creation of queue or topic -->
<auto-create-queues>false</auto-create-queues>
<auto-delete-queues>true</auto-delete-queues>
<auto-delete-created-queues>false</auto-delete-created-queues>
<auto-delete-queues-delay>30000</auto-delete-queues-delay>
<auto-delete-queues-message-count>0</auto-delete-queues-message-count>
<config-delete-queues>OFF</config-delete-queues>
<!-- We disable the automatic creation of address -->
<auto-create-addresses>false</auto-create-addresses>
<auto-delete-addresses>true</auto-delete-addresses>
<auto-delete-addresses-delay>30000</auto-delete-addresses-delay>
<config-delete-addresses>OFF</config-delete-addresses>
<management-browse-page-size>200</management-browse-page-size>
<default-purge-on-no-consumers>false</default-purge-on-no-consumers>
<default-max-consumers>-1</default-max-consumers>
<default-queue-routing-type>ANYCAST</default-queue-routing-type>
<default-address-routing-type>ANYCAST</default-address-routing-type>
<default-ring-size>-1</default-ring-size>
<retroactive-message-count>0</retroactive-message-count>
<enable-metrics>true</enable-metrics>
<!-- We automatically force group rebalance and a dispatch pause during group rebalance -->
<default-group-rebalance>true</default-group-rebalance>
<default-group-rebalance-pause-dispatch>true</default-group-rebalance-pause-dispatch>
<default-group-buckets>1024</default-group-buckets>
</address-setting>
</address-settings>
<!-- Define the protocols accepted -->
<!-- See: https://activemq.apache.org/components/artemis/documentation/latest/protocols-interoperability.html -->
<acceptors>
<!-- Acceptor for only CORE protocol -->
<!-- We enable the cache destination as recommended into the documentation. See: https://activemq.apache.org/components/artemis/documentation/latest/using-jms.html -->
<acceptor name="artemis">
tcp://0.0.0.0:61616?protocols=CORE,tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;useEpoll=true,cacheDestinations=true
</acceptor>
</acceptors>
<!-- Define how to connect to another broker -->
<!-- TODO : est-ce utile ? -->
<connectors>
<connector name="netty-connector">tcp://localhost:61616</connector>
<connector name="broker1-connector">tcp://localhost:61616</connector>
<connector name="broker2-connector">tcp://localhost:61617</connector>
</connectors>
<!-- Configure the High-Availability and broker cluster for the high-availability -->
<ha-policy>
<shared-store>
<master>
<failover-on-shutdown>true</failover-on-shutdown>
</master>
</shared-store>
</ha-policy>
<cluster-connections>
<cluster-connection name="gerico-cluster">
<connector-ref>netty-connector</connector-ref>
<static-connectors>
<connector-ref>broker1-connector</connector-ref>
<connector-ref>broker2-connector</connector-ref>
</static-connectors>
</cluster-connection>
</cluster-connections>
<!-- <cluster-user>cluster_user</cluster-user>-->
<!-- <cluster-password>cluster_user_password</cluster-password>-->
<!-- should the broker detect dead locks and other issues -->
<critical-analyzer>true</critical-analyzer>
<critical-analyzer-timeout>120000</critical-analyzer-timeout>
<critical-analyzer-check-period>60000</critical-analyzer-check-period>
<critical-analyzer-policy>HALT</critical-analyzer-policy>
<page-sync-timeout>72000</page-sync-timeout>
<!-- the system will enter into page mode once you hit this limit.
This is an estimate in bytes of how much the messages are using in memory
The system will use half of the available memory (-Xmx) by default for the global-max-size.
You may specify a different value here if you need to customize it to your needs.
<global-max-size>100Mb</global-max-size>
-->
<!-- Security configuration -->
<security-enabled>false</security-enabled>
<!-- Addresses and queues configuration -->
<!-- !!! DON'T FORGET TO UPDATE 'slave-broker.xml' FILE !!! -->
<addresses>
<address name="topic.test_rde">
<multicast>
<queue name="rde_receiver_1">
<durable>true</durable>
</queue>
<queue name="rde_receiver_2">
<durable>true</durable>
</queue>
</multicast>
</address>
<address name="queue.test_rde">
<anycast>
<queue name="queue.test_rde">
<durable>true</durable>
</queue>
</anycast>
</address>
</addresses>
</core>
</configuration>
The JMS configuration into the Spring Boot is the following:
#Bean
public DynamicDestinationResolver destinationResolver() {
return new DynamicDestinationResolver() {
#Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) throws JMSException {
if (destinationName.startsWith("topic.")) {
pubSubDomain = true;
}
else
pubSubDomain =false;
return super.resolveDestinationName(session, destinationName, pubSubDomain);
}
};
}
#Bean
public JmsListenerContainerFactory<?> queueConnectionFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer,
JmsErrorHandler jmsErrorHandler) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setPubSubDomain(false);
factory.setSessionTransacted(true);
factory.setErrorHandler(jmsErrorHandler);
return factory;
}
#Bean
public JmsListenerContainerFactory<?> topicConnectionFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer,
JmsErrorHandler jmsErrorHandler) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
factory.setPubSubDomain(true);
factory.setSessionTransacted(true);
factory.setSubscriptionDurable(true);
factory.setSubscriptionShared(true);
factory.setErrorHandler(jmsErrorHandler);
return factory;
}
The messages publisher:
#GetMapping("api/send")
public void sendDataToJms() throws InterruptedException {
OrderRest currentService = this.applicationContext.getBean(OrderRest.class);
for (Long i = 0L; i < 1L; i++) {
currentService.sendTopicMessage(i);
currentService.sendQueueMessage(i);
Thread.sleep(200L);
}
}
#Transactional
public void sendTopicMessage(Long id) {
Order myMessage = new Order("--" + id.toString() + "--", new Date());
jmsTemplate.convertAndSend("topic.test_rde", myMessage);
}
#Transactional
public void sendQueueMessage(Long id) {
Order myMessage = new Order("--" + id.toString() + "--", new Date());
jmsTemplate.convertAndSend("queue.test_rde", myMessage);
}
And the listeners:
#Transactional
#JmsListener(destination = "topic.test_rde", containerFactory = "topicConnectionFactory", subscription = "rde_receiver_1")
public void receiveMessage_rde_1(#Payload Order order, #Headers MessageHeaders headers, Message message, Session session) {
log.info("---> Consumer1 - rde_receiver_1 - " + order.getContent());
throw new ValidationException("Voluntary exception", "entity", List.of(), List.of());
}
#Transactional
#JmsListener(destination = "queue.test_rde", containerFactory = "queueConnectionFactory")
public void receiveMessage_rde_queue(#Payload Order order, #Headers MessageHeaders headers, Message message, Session session) {
log.info("---> Consumer1 - rde_receiver_queue - " + order.getContent());
throw new ValidationException("Voluntary exception", "entity", List.of(), List.of());
}
Is it normal that the redelivery policy and the DLQ mechanismd do only apply for a queue (anycat queue)?
Is it possible to also apply it for topic (multicast queue) and shared durable subscriptions?
If not, how can I have a topic behaviour but with redelivery and DLQ mechanisms? Should I use the divert solution of ActiveMQ?
Thank a lot for your help.
Regards.
I have found the problem. It comes from Atomikos that does not support the JMS v2.0 but only JMS 1.1.
So, it's not possible to get a shared durable subscription on a topic that support at the same time the 2-PC and the redelivery policy.
Setting up listener using IBM MQ in eclipse using wlp for java springboot application
Hi, I am trying to set up listener using wlp in my local in eclipse,
following is the code :
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<version>2.0</version>
</dependency>
java class:
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = test.queue)
public void recieve(Message message) {
log.info("inside message receiver");
try {
if (message instanceof TextMessage) {
message.acknowledge();
String json = ((TextMessage) message).getText();
/** To solve Json injection fortify issue **/
String sanitisedJsonMessage = JsonSanitizer.sanitize(json);
Gson gson = new Gson();
//business logic
} else {
log.error("ERROR ::: invalid message type");
message.acknowledge();
}
} catch (JsonSyntaxException | JMSException ex) {
log.error("ERROR: " + ex + ex.getMessage());
}
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
ConnectionFactory connectionfactory;
DefaultJmsListenerContainerFactory listenerFactory;
try {
log.info("inside listener factory");
#Cleanup
Context context = null;
listenerFactory = new DefaultJmsListenerContainerFactory();
context = new InitialContext();
connectionfactory = (ConnectionFactory) context.lookup(jms/ConnectionFactory);
listenerFactory.setConnectionFactory(connectionfactory);
listenerFactory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
} catch (NamingException ex) {
log.error("ERROR: error looking up queue connection factory jndi {}", ex);
}
return listenerFactory;
}
Now I tried to set up my server.xml in wlp as per ibm guidelines as follows:
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>javaee-7.0</feature>
<feature>jndi-1.0</feature>
<feature>jaxws-2.2</feature>
<feature>localConnector-1.0</feature>
<feature>transportSecurity-1.0</feature>
<feature>servlet-3.1</feature>
<feature>mdb-3.2</feature>
<feature>wmqJmsClient-2.0</feature>
<feature>jsp-2.3</feature>
</featureManager>
<!-- Define an Administrator and non-Administrator -->
<basicRegistry id="basic">
<user name="admin" password="adminpwd" />
<user name="nonadmin" password="nonadminpwd" />
</basicRegistry>
<!-- Assign 'admin' to Administrator -->
<administrator-role>
<user>admin</user>
</administrator-role>
<keyStore id="defaultKeyStore" password="Liberty" />
<httpEndpoint host="*" httpPort="9081" httpsPort="9444"
id="defaultHttpEndpoint" />
<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true" />
<applicationMonitor updateTrigger="mbean" />
<enterpriseApplication
id="mqtest-ear"
location="mqtest-ear.ear"
name="mqtest-ear" />
<variable name="wmqJmsClient.rar.location"
value="path\to\wlp\wmq\wmq.jmsra.rar" />
<jmsQueueConnectionFactory
jndiName="jms/ConnectionFactory">
<properties.wmqJms transportType="CLIENT"
hostName="<test.correcthostname>" port="<test.correctportname>"
channel="<test.correctchannelname>" queueManager="<test.correctqmgrname>" useSSL="true"
headerCompression="SYSTEM" messageCompression="RLE"
sslCipherSuite="SSL_RSA_WITH_AES_256_CBC_SHA256"
targetClientMatching="true" />
<connectionManager></connectionManager>
</jmsQueueConnectionFactory>
<jmsQueue id="JMSQueue" jndiName="jms/InQueue">
<properties.wmqJms baseQueueName="test.queue"
baseQueueManagerName="<test.correctqmgrname>" receiveConversion="CLIENT_MSG"
putAsyncAllowed="DESTINATION" targetClient="MQ"
readAheadAllowed="ENABLED" />
</jmsQueue>
<!-- <resourceAdapter
location="${wmqJmsClient.rar.location}" id="resourceAdapter">
</resourceAdapter> -->
<keyStore id="keyAndTrustStore" password="password"
location="path\to\keyandtruststore"
type="PKCS12">
</keyStore>
I have downloaded latest resource adapter and 9.0.0.8-IBM-MQ-Java-InstallRA.jar,
When I run the application , I get constant error:
2020-01-10 11:21:16 ERROR o.s.j.l.DefaultMessageListenerContainer - Could not refresh JMS Connection for destination 'test.queue' - retrying using FixedBackOff{interval=5000, currentAttempts=12, maxAttempts=unlimited}. Cause: MQJCA1011: Failed to allocate a JMS connection.
what Can I do to make listener work
After trying and troubleshoting a lot, I found a solution to set up ibm mq message listener using springboot and jms locally in websphere liberty server version 19.0.0.6.
I used following steps to make listener work:
download resource adaptor 9.0.0.8-IBM-MQ-Java-InstallRA.jar
Run the jar, and a folder named wmq will be generated with required .rar file in it.
Place the wmq folder inside wlp server : path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer
if using keystores for ssl , place trust and key store .jks or .p12 file inside wlp server path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer\resources\security
create a new file name jvm.options with following details:
-Dcom.ibm.mq.cfg.useIBMCipherMappings=false
-Djavax.net.debug="all"
-Djdk.tls.client.protocols="TLSv1.2"
-Dhttps.protocols="TLSv1.2"
-Djavax.net.ssl.trustStore="path\to\keyandtruststore.jks"
-Djavax.net.ssl.trustStorePassword="secret"
-Djavax.net.ssl.keyStore="path\to\keyandtruststore.jks"
-Djavax.net.ssl.keyStorePassword="secret"
save file inside following location : path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer
now add required dependencies in pom.xml
I am using spring boot version 2.1.4.release and following are important dependecies which will be required:
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>mq-jms-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
now change the wlp server.xml as follows:
<!-- Enable features -->
<featureManager>
<feature>webProfile-8.0</feature>
<feature>localConnector-1.0</feature>
<feature>jndi-1.0</feature>
<feature>jaxws-2.2</feature>
<feature>transportSecurity-1.0</feature>
<feature>wmqJmsClient-2.0</feature>
<feature>jsp-2.3</feature>
<feature>servlet-4.0</feature>
<feature>jms-2.0</feature>
<feature>javaee-8.0</feature>
</featureManager>
<!-- Define an Administrator and non-Administrator -->
<basicRegistry id="basic">
<user name="admin" password="adminpwd"/>
<user name="nonadmin" password="nonadminpwd"/>
</basicRegistry>
<!-- Assign 'admin' to Administrator -->
<administrator-role>
<user>admin</user>
</administrator-role>
<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>
<applicationMonitor updateTrigger="mbean"/>
<variable name="wmqJmsClient.rar.location" value="path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer\wmq\wmq.jmsra.rar"/>
<jmsQueueConnectionFactory jndiName="jms/ConnectionFactory" id="QueueConnectionFactory ">
<properties.wmqJms channel="channelName" headerCompression="NONE" hostName="host name" messageCompression="RLE" port="1414" queueManager="qmgrName" sslCipherSuite="TLS_RSA_WITH_AES_256_CBC_SHA256" targetClientMatching="true" transportType="CLIENT" temporaryModel="SYSTEM.DEFAULT.MODEL.QUEUE" pollingInterval="5s" rescanInterval="5s"/>
<connectionManager connectionTimeout="180s" maxPoolSize="20" minPoolSize="1" reapTime="180s" agedTimeout="0" maxIdleTime="30m"/>
</jmsQueueConnectionFactory>
<jmsQueue id="JMSQueue" jndiName="jms/testInQueue">
<properties.wmqJms putAsyncAllowed="DISABLED" readAheadAllowed="ENABLED" receiveConversion="CLIENT_MSG" targetClient="JMS" baseQueueName="queueName" baseQueueManagerName="qmgrName" failIfQuiesce="true" persistence="APP"/>
</jmsQueue>
<!-- <wmqJmsClient nativeLibraryPath="C:\Users\n78724\CRAS\resource adapter\lib"/> -->
<resourceAdapter id="resourceAdapter"
location="${wmqJmsClient.rar.location}">
<customize></customize>
</resourceAdapter>
<ssl id="keyAndTrustStore" keyStoreRef="defaultKeyStore" sslProtocol="TLSv1.2" trustStoreRef="defaultTrustStore"/>
<keyStore id="defaultKeyStore" location="path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer\resources\security\keyandtruststore.jks" password="secret"/>
<keyStore id="defaultTrustStore" location="path\to\wlp-webProfile8-19.0.0.6\wlp\usr\servers\defaultServer\resources\security\keyandtruststore.jks" password="secret"/>
<enterpriseApplication id="Test-ear" location="Test-ear.ear" name="Test-ear"/>
start the server
hope it helps.
In my application, I am consuming a third party web-service that is provided by my client.
I have developed my application on Spring and Hibernate framework and in one module I am consuming this third party web-service url. I have generated web-service stubs using
javab2-maven-plugin
The maven plugin in declared as below in my pom.xml file :
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- Package to store the generated file -->
<packageName>com.equifax.unsolicited.wsdl.stub</packageName>
<!-- Treat the input as WSDL -->
<wsdl>true</wsdl>
<!-- Input is not XML schema -->
<xmlschema>false</xmlschema>
<!-- The WSDL file that you saved earlier -->
<schemaFiles>Duk_CIS_Send_CreditStatus.wsdl</schemaFiles>
<!-- The location of the WSDL file -->
<schemaDirectory>${project.basedir}/src/main/wsdl</schemaDirectory>
<!-- The output directory to store the generated Java files -->
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<!-- Don't clear output directory on each run -->
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
And I am using the auto-generated JAXB java classes to call the web service. I have created a service bean which call the web service :
#Service("unsolicitResponseService")
public class UnsolicitResponseServiceImpl implements UnsolicitResponseService{
private static final Logger LOGGER = Logger.getLogger(UnsolicitResponseServiceImpl.class);
#Autowired
private WebServiceTemplate webServiceTemplate;
#Override
public void sendUnsolicitResponse() {
LOGGER.debug("Calling Duke Web Service to Send Unsolicit Response ... ");
try{
ObjectFactory objecFactory = new ObjectFactory();
CreditStatusMsgType creditStatusMessage = objecFactory.createCreditStatusMsgType();
creditStatusMessage.setMessageHeader(createMessageHeader(objecFactory));
//WRAP THE CLASS AS THE INSTANCE OF JAXBELEMENT OTHERWISE IT WILL THROW MISSING ROOTELEMENT ERROR
JAXBElement<CreditStatusMsgType> creditStatusMessageJaxbElement = objecFactory.createSendCreditStatus(creditStatusMessage);
//CREATE STRING WRITER TO LOG THE REQUEST
Object response = this.webServiceTemplate.marshalSendAndReceive(creditStatusMessageJaxbElement);
LOGGER.debug("Jumio Web Service Response Reponse :"+response);
LOGGER.debug("Unsolicit Response sent to Duke Successfully.");
}catch(Exception ex){
LOGGER.error("Exception generated while calling Web Service to send unsolicit Response : "+ex.getLocalizedMessage(),ex);
}
}
Below is the xml configuration where I have declared interceptors to log the request and response :
<?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:sws="http://www.springframework.org/schema/web-services"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/web-services
http://www.springframework.org/schema/web-services/web-services-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- DEFINE SOAP VERSION USED BY A WSDL -->
<bean id="soapMessageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
<property name="soapVersion">
<!-- FOR TEXT/XML -->
<util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_11"/>
</property>
</bean>
<!-- LOCATION OF THE GENERATED JAVA FILEs -->
<oxm:jaxb2-marshaller id="marshaller" contextPath="com.equifax.unsolicited.wsdl.stub"/>
<!-- CONFIGURE THE SPRING WEB SERVICE -->
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="soapMessageFactory"/> <property name="marshaller" ref="marshaller"/>
<property name="unmarshaller" ref="marshaller"/>
<property name="defaultUri" value="https://partnerstg.duke-energy.com:4443/DukCISSendCreditStatus?wsdl"/>
</bean>
<sws:interceptors>
<bean id="jumioPeyLoadLoggingInterceptor" class="com.test.datasource.logging.interceptor.PayloadLoggingInterceptor">
</bean>
<bean id="jumioSOAPLoggingInterceptor" class="com.test.datasource.logging.interceptor.SOAPLoggingInterceptor">
</bean>
</sws:interceptors>
</beans>
And I also have added and new logging category to enable the logger level to DEBUG mode :
Above code is calling the web service successfully. But the interceptors are not getting called. So I am not able to log the XML request and response.
Here, I am assuming that, these interceptors will not work while consuming the service. Let me know if I am wrong here.
I am referring Spring Web Service from HERE . This website has given explanation interceptors while publishing a web-service.
Kindly let me know should we use this interceptors while consuming a web-service ? Or How should I print request and response which are JAXB-ELEMENT ?
I am adding here solution which I have implemented. There are two ways by which we can implement this solution. I have implemented the second one from below list using JAXBContext and Marshaller.
1> Log Request/Response By interceptor.
We can not use PayloadLoggingInterceptor or SOAPLoggingInterceptor when we are consuming the web service.
We need to use ClientInterceptor when we are consuming the web service. ClientInterceptor is implemented by PayloadValidatingInterceptor class which is used to intercept the request/response and validate the it based on xsd schema.
For that we need to provide interceptor reference as below :
<bean id="MyPayloadValidatingInterceptor" class="com.equifax.ic.datasource.jumio.ws.logging.interceptor.JumioPayloadValidatingInterceptor">
<property name="schema" value="file:WebContent/WEB-INF/schemas/account-balance-service.xsd" />
<property name="validateRequest" value="false" />
<property name="validateResponse" value="false" />
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="soapMessageFactory"/> <property name="marshaller" ref="marshaller"/>
<property name="unmarshaller" ref="marshaller"/>
<property name="defaultUri" value="https://partnerstg.duke-energy.com:4443/DukCISSendCreditStatus?wsdl"/>
<property name="interceptors">
<list>
<ref bean="MyPayloadValidatingInterceptor"/>
</list>
</property>
</bean>
2> Log Request/Response by using JAXBContext
This is the solution which I have implemented in my application as we should not use PayloadValidatingInterceptor only to log reqeust/response.
private void logJAXBRequest(JAXBElement<CreditStatusMsgType> creditStatusMessageJaxbElement){
LOGGER.debug("Logging Web Service Request ...");
StringWriter writer = null;
StreamResult streamResult = null;
StringBuffer buffer = null;
try{
writer = new StringWriter();
streamResult = new StreamResult(writer);
JAXBContext jaxbContext = JAXBContext.newInstance(CreditStatusMsgType.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(creditStatusMessageJaxbElement, streamResult);
buffer = writer.getBuffer();
LOGGER.debug("JAXB Webservice Request : "+ buffer.toString());
writer.close();
}catch(Exception ex){
LOGGER.error("Exception generated while creating XML Logs of JAXB Request :",ex);
}
}
He everyone, colleagues.
There are two main ways to show XML requests / responses:
First of all you have to add log4j dependency into your pom.xml file:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
Then you have to place log4j.properties file into a classpath of your application. When I develop SOAP services I often use Spring WS Maven artefact. Unfortunately, a usual resources folder is not created from scratch and you have to create it manually. Then you place log4j.properties file there. The contents of log4j config depends on the approach you want to use (see items below). Obtained structure is following:
Use a standard Message Logging and Tracing approach & log4j.properties file. Nothing should be configured, developed, written except log4j config file contents. The contents of log4j config should be the following (use these contents as is):
log4j.rootCategory=DEBUG, stdout
log4j.logger.org.springframework.ws.client.MessageTracing.sent=TRACE
log4j.logger.org.springframework.ws.client.MessageTracing.received=DEBUG
log4j.logger.org.springframework.ws.server.MessageTracing=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%p [%c{3}] %m%n
Use a PayloadLoggingInterceptor & log4j.properties file. Some config changes should be applied, but this approach is more flexible, as for me. First of all you have to add PayloadLoggingInterceptor into a MessageDispatcherServlet config 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:context="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.ln.springws"/>
<sws:annotation-driven/>
<sws:interceptors>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor" />
</sws:interceptors>
<sws:dynamic-wsdl id="holiday" portTypeName="HumanResource" locationUri="http://localhost:8080/holidayService/" targetNamespace="http://spring-ws-holidays.com/hr/definitions">
<sws:xsd location="/WEB-INF/hr.xsd"/>
</sws:dynamic-wsdl>
</beans>
And at last place the following contents to log4j.properties file:
log4j.rootLogger=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-5p [%c] - <%m>%n
And as a result of both approaches you will have something like that in a console:
Trying to get my Liberty profile server to create a transacted jmsConnectionFactory to my instance of Websphere message queue.
Liberty profile v 8.5.5.5
Websphere MQ 7.x
I've tried to change the jmsQueueConnectionFactory to jmsXAQueueConnectionFactory but it does not help -> Then it seems to just ignore it and doesn't connect to the MQ
server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>wmqJmsClient-1.1</feature>
<feature>jndi-1.0</feature>
<feature>jsp-2.2</feature>
<feature>localConnector-1.0</feature>
</featureManager>
<variable name="wmqJmsClient.rar.location" value="D:\wlp\wmq\wmq.jmsra.rar"/>
<jmsQueueConnectionFactory jndiName="jms/wmqCF" connectionManagerRef="ConMgr6">
<properties.wmqJms
transportType="CLIENT"
hostName="hostname"
port="1514"
channel="SYSTEM.DEF.SVRCONN"
queueManager="QM"
/>
</jmsQueueConnectionFactory>
<connectionManager id="ConMgr6" maxPoolSize="2"/>
<applicationMonitor updateTrigger="mbean"/>
<application id="App"
location="...\app.war"
name="App" type="war"/>
<!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint id="defaultHttpEndpoint"
httpPort="9080"
httpsPort="9443"/>
</server>
log
2015-04-23 17:07:14,981 [JmsConsumer[A0]] WARN ultJmsMessageListenerContainer - Setup of JMS message listener invoker failed for destination 'A0' - trying to recover. Cause: Could not commit JMS transaction; nested exception is com.ibm.msg.client.jms.DetailedIllegalStateException: JMSCC0014: It is not valid to call the 'commit' method on a nontransacted session. The application called a method that must not be called on a nontransacted session. Change the application program to remove this behavior.
2015-04-23 17:07:14,983 [JmsConsumer[A0]] INFO ultJmsMessageListenerContainer - Successfully refreshed JMS Connection
Camel code
public static JmsComponent mqXAComponentTransacted(InitialContext context, String jndiName) throws JMSException, NamingException {
return JmsComponent.jmsComponentTransacted((XAQueueConnectionFactory) context.lookup(jndiName));
}
Ended up with:
public static JmsComponent mqXAComponentTransacted(InitialContext context, String connectionFactoryJndiName, String userTransactionJndiName) throws JMSException, NamingException {
LOG.info("Setting up JmsComponent using jndi lookup");
final JtaTransactionManager jtaTransactionManager = new JtaTransactionManager((UserTransaction) context.lookup(userTransactionJndiName));
final ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup(connectionFactoryJndiName);
final JmsComponent jmsComponent = JmsComponent.jmsComponentTransacted(connectionFactory, (PlatformTransactionManager) jtaTransactionManager);
jmsComponent.setTransacted(false);
jmsComponent.setCacheLevel(DefaultMessageListenerContainer.CACHE_NONE);
return jmsComponent;
}
Platform: Shiro 1.1.0, Spring 3.0.5
I'm trying to secure the MVC Controller methods using Shiro annotation. However something is wrong with annotations. Regular calls are just working OK. There is nothing specific in Shiro debug also.
My shiro configuration:
<!-- Security Manager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="sessionMode" value="native" />
<property name="realm" ref="jdbcRealm" />
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- Caching -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="ehCacheManager" />
</bean>
<bean id="ehCacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
<bean id="sessionDAO"
class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO" />
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionDAO" ref="sessionDAO" />
</bean>
<!-- JDBC Realm Settings -->
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="name" value="jdbcRealm" />
<property name="dataSource" ref="dataSource" />
<property name="authenticationQuery"
value="SELECT password FROM system_user_accounts WHERE username=? and status=1" />
<property name="userRolesQuery"
value="SELECT role_name FROM system_roles r, system_user_accounts u, system_user_roles ur WHERE u.user_id=ur.user_id AND r.role_id=ur.role_id AND u.username=?" />
<property name="permissionsQuery"
value="SELECT permission_name FROM system_roles r, system_permissions p, system_role_permission rp WHERE r.role_id=rp.role_id AND p.permission_id=rp.permission_id AND r.role_name=?" />
<property name="permissionsLookupEnabled" value="true"></property>
</bean>
<!-- Spring Integration -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after
the lifecycleBeanProcessor has run: -->
<bean id="annotationProxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" />
<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Secure Spring remoting: Ensure any Spring Remoting method invocations
can be associated with a Subject for security checks. -->
<bean id="secureRemoteInvocationExecutor"
class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Shiro filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/dashboard" />
<property name="unauthorizedUrl" value="/error" />
<property name="filterChainDefinitions">
<value>
<!-- !!! Order matters !!! -->
/authenticate = anon
/login = anon
/logout = anon
/error = anon
/** = authc
</value>
</property>
</bean>
I can get the following working correctly:
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
if (!SecurityUtils.getSubject().isPermitted("hc:viewPatient")){
logger.error("Operation not permitted");
throw new AuthorizationException("No Permission");
}
}
But the below doesn't work:
#RequiresPermissions("hc:patientView")
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
Am I missing something? Please help.
You were absolutely right. After seeing your comment, I started giving it a thought. Well then I found out that it was NOT an implementation problem with Shiro, but the jar dependecies were not properly configured. Shiro's pom.xml should have dependency for cglib2 too.
So the below changes worked for me :
Include all these four jar files.
aspectjrt-1.6.11.jar,
aspectjweaver-1.6.12.jar,
cglib-2.2.2.jar,
asm-3.3.1.jar,
If you are using maven then :
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
And finally placing the aop:aspectj-autoproxy in the webApplicationContext.xml
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Annotation, so that it's easier to search controllers/components -->
<context:component-scan base-package="com.pepsey.soft.web.controller"/>
Note : The above two configuration should be placed together in the same spring-webApplicationContext.xml. Otherwise it won’t work. Moreover remove context:annotation-config if you have used it in your config. context:component-scan already scans all annotations.
Once you start testing , set your log4j to debug or (better) trace mode. Whenever you are starting your server you will find somewhere the following entry in your logs :
08:16:24,684 DEBUG AnnotationAwareAspectJAutoProxyCreator:537 -
Creating implicit proxy for bean 'userController' with 0 common
interceptor and 1 specific interceptors
Guess Shiro was built when Spring 2.0 was in place. Shiro’s annotations (RequiresRoles etc…) works well for the spring container managed beans (service layer), but it does not work with #Controller annotation. This is due to the fact that #Controller is being component scanned by spring framework. I used AOP to resolve the issue. Below is the solution which worked for me.
For the below solution to work you have to include the below four jars:
aspectjrt-1.6.11.jar
aspectjweaver-1.6.12.jar
cglib-2.2.2.jar
asm-3.3.1.jar
If you are using maven then below configuration would be helpful.
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
Below is a controller class
import org.apache.shiro.authz.annotation.RequiresRoles;
#Controller
public class PatientController {
#RequiresRoles(“admin,warden”)
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
return “somePatientFormJsp”;
}
}
Create the below Aspect for the annotation (RequiresRoles). You can use the same principle to create pointcuts for RequiresPermission.
import java.util.Arrays;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class WebAuthorizationAspect {
#Before("#target(org.springframework.stereotype.Controller) && #annotation(requiresRoles)")
public void assertAuthorized(JoinPoint jp, RequiresRoles requiresRoles) {
SecurityUtils.getSubject().checkRoles(Arrays.asList(requiresRoles.value()));
}
}
In your spring-webApplicationContext.xml wherever you have mentioned
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Annotation, so that it's easier to search controllers/components -->
<context:component-scan base-package="com.example.controller"/>
Note : The above two configuration should be placed together in the same spring-webApplicationContext.xml. Otherwise it won’t work. Moreover remove context:annotation-config if you have used it in your config. context:component-scan already scans all annotations.
If you're avoiding Spring XML and using primarily Java and annotation configuration, the easiest way to fix this is to add
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
to all your #Controller classes. You need cglib on the classpath.
I have only used spring-hibernate example from sample. To use annotations like #RequiresPermissions and others I tried configuration from shiro manual, configuration from this post, but I was either unsuccessful to compile or run the valid urls. So I only commented all the #RequiresPermissions from ManageUserController and started to use it in service implementation. E.g In DefaultUserService in getAllUsers method I added the annotation #RequiresPermissions("user:manage"). Magically now the application works as expected. Whenever the url manageUsers is called it displays the list page if the user has role user:manage and throws the user to /unauthorized if the user don't have that permission.
I have even configured the application to use mysql instead. To make the permissions independent of roles according to new RBAC(http://www.stormpath.com/blog/new-rbac-resource-based-access-control) I have created a new class called Permission as
#Entity
#Table(name = "permissions")
#Cache(usage= CacheConcurrencyStrategy.READ_WRITE)
public class Permission {
#Id
#GeneratedValue
private Long id;
private String element;
private String description;
// setter and getter
Now Role class is configured as
#CollectionOfElements
#JoinTable(name="roles_permissions")
#Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public Set<Permission> getPermissions() {
return permissions;
}
And finally SampleRealm as
for (Role role : user.getRoles()) {
info.addRole(role.getName());
System.out.println("Roles " + role.getName());
// Get permissions first
Set<Permission> permissions = role.getPermissions();
Set<String> permissionsStrings = new HashSet<String>();
for (Permission permission : permissions) {
permissionsStrings.add(permission.getelement());
System.out
.println("Permissions " + permission.getelement());
}
info.addStringPermissions(permissionsStrings);
}
It creates five tables as
| permissions |
| roles |
| roles_permissions |
| users |
| users_roles |
And permissions is independent of any other. According to new RBAC you have both ways (explicit and implicit) way of authorising resources.
You need to write the AuthorizationAttributeSourceAdvisor to enable Shiro's annotations bean as per the Shiro documentation
If you have written ShiroConfiguration class, make sure you include this:
#Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
#Bean
#ConditionalOnMissingBean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) {
// This is to enable Shiro's security annotations
AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
sourceAdvisor.setSecurityManager(securityManager);
return sourceAdvisor;
}
#ConditionalOnMissingBean
#Bean(name = "defaultAdvisorAutoProxyCreator")
#DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
Example ShiroConfiguration on Github
I had the same problem. My fix was changing my jersey version from 2.2 to 2.22.2 and all #RequiresPermissions worked on my controllers.