I'm currently implementing a spring boot application with quite a few dependencies on google cloud services like PubSub. The spring boot autoconfiguration creates a number of beans for me.
For example a MessagingGateway implementation and a PubSubTemplate.
Now I have the following artifacts:
#Service
public class MyServiceImpl implements MyService {
private final PubsubOutboundGateway messagingGateway;
public MyServiceImpl(PubsubOutboundGateway messagingGateway) {
this.messagingGateway = messagingGateway;
}
#Override
public void sendToPubSub(String s) {
messagingGateway.sendTest(s);
}
}
#MessagingGateway
#Component
public interface PubsubOutboundGateway {
#Gateway(requestChannel = "myChannel" )
void sendTest(String test);
}
#Configuration
public class Channels {
#Bean
#ServiceActivator(inputChannel = "myChannel")
public MessageHandler messageSender(PubSubTemplate pubsubTemplate) {
return new PubSubMessageHandler(pubsubTemplate, "my-topic");
}
}
When I turn off pubsub for local development, I get the following error
Consider defining a bean of type 'com.google.cloud.spring.pubsub.core.PubSubTemplate' in your configuration.
But what I really want is a local PubsubOutboundGateway that just prints the messages.
I can achieve this by adding #Profile("!local") to PubsubOutboundGateway and Channels and implement a PubsubOutboundGatewayLocalImpl. But this seems like a hack.
How can perform local development without having an active GCP key etc. setup? Or does that just hinder development and I should use an active key?
I'd suggest to use the Pub/Sub emulator, in order to get as close to the remote environment as possible.
Related
I'm working on a project right now and I want to use SOAP web service with dependency injection in my Spring Boot project, but I'm having a problem.
As you can see below, I'm injecting using #Autowired annotation, but it shows this problem:
Could not autowire. No beans of 'KNIKPSPublicSoap' type found.
#Service
#RequiredArgsConstructor
public class JobSeekerManager implements JobSeekerService {
private final JobSeekerDal jobSeekerDal;
#Autowired
private KNIKPSPublicSoap soapClientWS;
#Override
public void save(JobSeeker jobSeeker) {
//logica
this.jobSeekerDal.save(jobSeeker);
}
#Override
public List<JobSeeker> findAll() {
return this.jobSeekerDal.findAll();
}
}
I have a Spring Boot application which needs to occasionally publish messages to GCP PubSub. I implemented it following the instructions on the spring boot page (https://spring.io/guides/gs/messaging-gcp-pubsub/) so I have implemented the following configuration file:
#Configuration
public class PubSubConfiguration {
#Value("${myprog.pubsub.sms-topic}")
private String topic;
#Bean
#ServiceActivator(inputChannel = "pubsubOutputChannel")
public MessageHandler messageSender(PubSubTemplate pubsubTemplate) {
return new PubSubMessageHandler(pubsubTemplate, this.topic);
}
#MessagingGateway(defaultRequestChannel = "pubsubOutputChannel")
public interface PubsubOutboundGateway {
void sendToPubsub(String text);
}
}
From my rest controller, I autowire the message gateway and call sendToPubsub:
#RequestMapping("/api/stuff")
#RestController
public class StuffController {
PubSubConfiguration.PubsubOutboundGateway messagingGateway;
#Autowired
public StuffController(#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") PubSubConfiguration.PubsubOutboundGateway messagingGateway) {
this.messagingGateway = messagingGateway;
}
#RequestMapping(method = RequestMethod.POST, path = "/go")
public ResponseEntity<String> send() {
messagingGateway.sendToPubsub("TEST");
return new ResponseEntity<>("Ok!", HttpStatus.OK);
}
}
This works, however due to our particular use case I would like to respond with an error if publishing fails. If, for example, I configure a non-existent topic I would like to return a 500 error whereas it currently returns 200 and throws an exception asynchronously later. Is there any way I can access a future at the point of publishing?
Spring Cloud GCP PubSub implementation uses Spring Integration framework and rely on it. For this, your send to PubSub method have to throw an exception as described in the Spring integration documentation
#MessagingGateway(defaultRequestChannel = "pubsubOutputChannel")
public interface PubsubOutboundGateway {
void sendToPubsub(String text) throws MessagingException;
}
I'm using Spring 3.2.5 without full new JSR-356 WebSockets support.
I would like to have singleton-bean reference in my #ServerEndpoint WebSocket server, which is instantiated by servlet container itself, not in Spring context.
What is the clean way to do it?
My current solution: I made #Service singleton bean with instance in static field:
#Service
public class WebSocketSupportBean {
private volatile static WebSocketSupportBean instance = null;
public static WebSocketSupportBean getInstance() {
return instance;
}
public WebSocketSupportBean() {
instance = this;
}
and just getting it in #ServerEndpoint by static method, disconnecting user if null returned (if bean not jet created during server startup but user connects):
You can setup websockets with spring framework 3.x
I developed a small proof-of-concept application to demonstrate how, based on Rossen Stoyanchev's SpringConfiguration released with spring-core 4.0.
The application sets up a websocket server endpoint with uri /wstest which will use a #Autowired spring bean to select a greeting word and reply to a websocket message.
The websocket connection is initiated and messages sent by an html page (index.html) running in a browser that supports websockets.
The Endpoint registration is made by a ServletContextListener at context initialization and when the endpoint is instantiated it will be wired with spring:
#WebListener
public class MyApplication implements ServletContextListener {
private final static String SERVER_CONTAINER_ATTRIBUTE = "javax.websocket.server.ServerContainer";
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext container = sce.getServletContext();
final ServerContainer serverContainer = (ServerContainer) container.getAttribute(SERVER_CONTAINER_ATTRIBUTE);
try {
serverContainer.addEndpoint(new MyEndpointConfig(MyEndpoint.class, "/wstest"));
} catch (DeploymentException e) {
e.printStackTrace();
}
}
}
And the Endpoint is:
#Component
public class MyEndpoint extends Endpoint {
#Autowired
MyService myService;
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MyMessageHandler(session));
}
class MyMessageHandler implements MessageHandler.Whole<String> {
final Session session;
public MyMessageHandler(Session session) {
this.session = session;
}
#Override
public void onMessage(String message) {
try {
String greeting = myService.getGreeting();
session.getBasicRemote().sendText(greeting + ", got your message (" + message + "). Thanks ! (session: " + session.getId() + ")");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Checkout the full source and ready to run example on my Github page.
You have to add bean definition in the configuration of spring.
The solution i found to integrate JSR 356 websocket #ServerEndpoint is to turn off the Servlet container's scan for WebSocket endpoints by spring which can be done by registering #Bean in your Spring Configuration. By this spring not overrides normal JSR 356 websocket by spring STOMP websocket which is the part of the websocket.
#ServerEndpoint(value="/chatMessage")
public class ChatEndpoint{
// Normal websocket body goes here.
}
Adding Beans in your Configuration as:
#Configuration
public class WebsocketConfig{
#Bean
public ChatEndpoint chatEndpoint(){
return new ChatEndpoint();
}
// main one is ServerEndpointExporter which prevents Servlet container's scan for WebSocket
#Bean
public ServerEndpointExporter endpointExporter(){
return new ServerEndpointExporter();
}
}
This all done for you. But you should remove configurator = SpringConfigurator.class from #ServerEndpoint.
I am using Spring Websocket 4.0.0 and it works fine.
You can also see this Link.
If you alright then follow this Link also for concept.
Note that, Normally you should make websocket configuration separately from the main configuration of your spring.
Try
#ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)
And add maven dependency
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
You can make your #ServerEndpoint object extend SpringBeanAutowiringSupport. Then just make it aware of beans that gets constructed within a Spring-based web application this way:
#PostConstruct
public void init() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
This way #Autowired annotation will worl correctly:
#Autowired MyService myService;
try this,it works for me
#Component
#ServerEndpoint(value = "/instantMessageServer",configurator = SpringConfigurator.class)
public class InstantMessageServer{
private static IChatService chatService;
#Autowired
public InstantMessageServer(IChatService chatService){
this.chatService = chatService;
}
public InstantMessageServer(){}
}
I found this solution on https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support
but there is one more glitch,the class annotated with #ServerEndpoint cant acquire httpsession with SpringConfigurator,there is no a override of method modifyhandler in it.Maybe we create a seperate Configurator extends SpringConfigurator and override that method would be a workaroud.
It is better to build a real-time web application with spring-websocket and messaging api,I think.
public class ModifiedServerEndpointConfigurator extends SpringConfigurator{
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
super.modifyHandshake(sec, request, response);
}
}
Instead of measuring by inserting code into each method of interest I intended to use the provided #Timed annotation. But the metrics do not show any corresponding values:
This is my code, the idea is having the execution times of the contained SQL being put into the metrics.
#Component
public class Foo {
private JdbcTemplate jdbcTemplate;
#Autowired
public Metadata(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Timed(name = "myapp.get.foo")
public boolean getFoo(final String foo ) {
String foo = jdbcTemplate.query( ...
}
}
The problem with #Timed not showing up is probably because Spring Boot only supports Counter and Gauge.
But #Gauge, #Metered and #Counted don't work either.
What am I missing in order to make at least those metrics annotation work that are supported by Spring Boot ? (1.3.1 in my tests)
You would probably want to take a look at
http://www.ryantenney.com/metrics-spring/
This projects eases integration of dropwizard metrics into Spring Boot projects.
It will automatically create metrics and proxies beans to make the #Timed annotation work.
I ended up not using the annotations and simply implementing MetricSet on beans that I want to instrument. Especially with timers, using one is not that hard.
#Service
public class MyBean implements MetricSet {
Timer putTimer = new Timer();
public void someService() {
try(Context context = putTimer.time()) {
....
}
}
#Override
public Map<String, Metric> getMetrics() {
Map<String,Metric> metrics=new HashMap<>();
metrics.put("mymetricname",putTimer);
return metrics;
}
}
#Slf4j
#Service
public class MetricsContextListener implements ApplicationListener<ContextRefreshedEvent> {
private final MetricRegistry metricRegistry;
public MetricsContextListener(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, MetricSet> beans = event.getApplicationContext().getBeansOfType(MetricSet.class);
for(Map.Entry<String, MetricSet> e: beans.entrySet()) {
log.info("registering " + e.getKey() + " " + e.getValue().getClass().getName());
metricRegistry.register(e.getKey(), e.getValue());
}
}
}
Also you'll probably want to create your own MetricRegistry. We had some issues with spring boot creating one but our spring tests failing because it was missing. If you create one yourself, that problem goes away. Simply add this to one of your #Configuration classes
#Bean
public MetricRegistry metricRegistry() {
// explicitly register a metricRegistry so we can run our spring tests without relying on spring boot creating
// one for us. Spring boot seems to do the right thing either way.
// Used to register codahale metrics from MetricSet implementing beans.
return new MetricRegistry();
}
With all this in place, Spring boot does the right thing and adds the metrics to /metrics.
I have a Java server which serves my clients (Not application server).
Now I'm interested to add REST support. I've initialized a Jetty server and created few REST resources.
My question is: How can I pass parameters at the creation of the REST resources?
Normally I would prefer in the constructor of each resource, but I don't control it.
I understand there is a way to inject dependencies. How to do it using Jersey 2.5??
Thank you!
Define your Application
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new FacadeBinder());
register(JacksonFeature.class);
register(MyEndpoint.class);
}
Configure injection
public class FacadeBinder extends AbstractBinder {
#Override
protected void configure() {
bind(MyManager.class).to(MyManager.class);
}
}
Inject configured classes in your endpoint
#Path("/jersey")
public class MyEndpoint {
#Inject
MyManager myManager;
...
}
I'm not sure to understand what do you mean with dependencies.
You should check this: https://jersey.java.net/documentation/latest/user-guide.html#d0e1810
Another option besides using dependency injection is to instantiate and register the REST endpoint yourself. Jersey allows you to do this in a very similar fashion as dependency injection as shown in Dymtro's example. Borrowing liberally from Dymtro, define your endpoint:
#Path("/jersey")
public class MyEndpoint {
private MyManager myManager;
public MyEndpoint(MyManager myManager) {
this.myManager = myManager;
}
....
}
Define your application:
public class MyApplication extends ResourceConfig {
public MyApplication(MyManager myManager) {
register(JacksonFeature.class);
register(new MyEndpoint(myManager));
....
}
}