Camel MQTT Configuration options DSL route URL parameter with bean value not working - spring-boot

I ma trying to test pub/sub from an external broker ( AWs IoT ); started off of the camel-example-spring-boot example project and added thecamel-mqtt-starter. Everything seems to work fine until I try to define the mqtt routes. I am having issues with configuring the sslContext url parameter :
#Configuration
public class AppConfig {
#Bean(name="awsiotsslcontext")
SSLContext awsiotsslcontext(){
SSLContext sslContext = null;
try{
ClassLoader cl = this.getClass().getClassLoader();
InputStream is = cl.getResourceAsStream("/cert/myApp.cert.pem");
// You could get a resource as a stream instead.
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate)cf.generateCertificate(is);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null); // You don't need the KeyStore instance to come from a file.
ks.setCertificateEntry("caCert", caCert);
tmf.init(ks);
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
} catch (Exception e){
}
return sslContext;
}
}
And then in my route builder :
#Component
public class SampleCamelRouter extends RouteBuilder {
#Autowired
SSLContext awsiotsslcontext;
#Override
public void configure() throws Exception {
from("timer://foo?repeatCount=0&delay=5000&fixedRate=true&period=10s")
.setBody(simple("TEST MESSAGE"))
.to("mqtt:awsiot?host=ssl://{{aws.iot.host}}:8883&publishTopicName={{aws.iot.sub.topic}}&sslContext=#awsiotsslcontext").log("Sent :"+body().convertToString().toString());
from("mqtt:awsiot?host=ssl://{{aws.iot.host}}:8883&subscribeTopicName={{aws.iot.sub.topic}}&sslContext=#awsiotsslcontext").log("Recieved : "+body().convertToString().toString());
}
}
getting the following error :
java.lang.IllegalArgumentException: Could not find a suitable setter
for property: sslContext as there isn't a setter method with same
type: java.lang.String nor type conversion possible: No type converter
available to convert from type: java.lang.String to the required type:
javax.net.ssl.SSLContext with value #awsiotsslcontext
I believe this is a simple endpoint configuration issue, but tried various things and nothing seems to work. Having the # with the bean name should have camel to look up in the registry for the bean but here it recognizes it as String ? Any workaround here ?

This was an issue on camel route configuration; when I configured my route under a #Configuration instead of having under #Component as suggested by the documentation, it doesn't complain about bean definition with '#' in the URI; I would expect in a Spring Boot Application the beans would load before the routes by default:
#Bean
RouteBuilder awsIoTRoute() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("timer://foo?repeatCount=0&delay=5000&fixedRate=true&period=17s")
.setBody(simple("TEST MESSAGE"))
.to("mqtt:awsIoTPublisher?host=ssl://{{aws.iot.host}}:8883&publishTopicName={{aws.iot.pub.topic}}&clientId={{aws.iot.pub.clientId}}&sslContext=#sslContext")
.log("Sent :"+body().convertToString().toString());
from("mqtt:awsIoTReciever?host=ssl://{{aws.iot.host}}:8883&subscribeTopicName={{aws.iot.sub.topic}}&clientId={{aws.iot.sub.clientId}}&sslContext=#sslContext").log("Recieved : "+body().convertToString());
}
};
}

Related

Spring Integration: connection to multiple MQ servers by config

I do have a Spring Boot 5 application and I also have it running against one IBM MQ server.
Now we want it to connect to three or more MQ servers. My intention is now to just add XY connection infos to the environment and then I get XY MQConnectionFactory beans and al the other beans that are needed for processing.
At the moment this is what I have:
#Bean
#Qualifier(value="MQConnection")
public MQConnectionFactory getIbmConnectionFactory() throws JMSException {
MQConnectionFactory factory = new MQConnectionFactory();
// seeting all the parameters here
return factory;
}
But this is quite static. Is there an elegant way of doing this?
I stumbled about IntegrationFlow. Is this a possibly working solution?
Thanks for all your tipps!
KR
Solution
Based on Artem Bilan's response I built this class.
#Configuration
public class ConnectionWithIntegrationFlowMulti {
protected static final Logger LOG = Logger.create();
#Value("${mq.queue.jms.sources.queue.queue-manager}")
private String queueManager;
#Autowired
private ConnectionConfig connectionConfig;
#Autowired
private SSLSocketFactory sslSocketFactory;
#Bean
public MessageChannel queureader() {
return new DirectChannel();
}
#Autowired
private IntegrationFlowContext flowContext;
#PostConstruct
public void processBeanDefinitionRegistry() throws BeansException {
Assert.notEmpty(connectionConfig.getTab().getLocations(), "At least one CCDT file locations must be provided.");
for (String tabLocation : connectionConfig.getTab().getLocations()) {
try {
IntegrationFlowRegistration theFlow = this.flowContext.registration(createFlow(tabLocation)).register();
LOG.info("Registered bean flow for %s with id = %s", queueManager, theFlow.getId());
} catch (JMSException e) {
LOG.error(e);
}
}
}
public IntegrationFlow createFlow(String tabLocation) throws JMSException {
LOG.info("creating ibmInbound");
return IntegrationFlows.from(Jms.messageDrivenChannelAdapter(getConnection(tabLocation)).destination(createDestinationBean()))
.handle(m -> LOG.info("received payload: " + m.getPayload().toString()))
.get();
}
public MQConnectionFactory getConnection(String tabLocation) throws JMSException {
MQConnectionFactory factory = new MQConnectionFactory();
// doing stuff
return factory;
}
#Bean
public MQQueue createDestinationBean() {
LOG.info("creating destination bean");
MQQueue queue = new MQQueue();
try {
queue.setBaseQueueManagerName(queueManager);
queue.setBaseQueueName(queueName);
} catch (Exception e) {
LOG.error(e, "destination bean: Error for integration flow");
}
return queue;
}
}
With Spring Integration you can create IntegrationFlow instances dynamically at runtime. For that purpose there is an IntegrationFlowContext with its registration() API. The returned IntegrationFlowRegistrationBuilder as a callback like:
/**
* Add an object which will be registered as an {#link IntegrationFlow} dependant bean in the
* application context. Usually it is some support component, which needs an application context.
* For example dynamically created connection factories or header mappers for AMQP, JMS, TCP etc.
* #param bean an additional arbitrary bean to register into the application context.
* #return the current builder instance
*/
IntegrationFlowRegistrationBuilder addBean(Object bean);
So, your MQConnectionFactory instances can be populated alongside with the other flow, used as references in the particular JMS components and registered as beans, too.
See more info in docs: https://docs.spring.io/spring-integration/docs/5.2.3.RELEASE/reference/html/dsl.html#java-dsl-runtime-flows
If you are fine with creating them statically, you can create the beans as you are now (each having a unique qualifier), but you can access them all dynamically in your services / components by having an #Autowired List<MQConnectionFactory> field or #Autowired Map<String, MQConnectionFactory> field. Spring will automatically populate the fields with all of the beans of type MQConnectionFactory
In the the Map implementation, the String will be the qualifier value.
If you also want to create the beans dynamically based on some properties, etc, it gets a little more complicated. You will need to look into something along the lines of instantiating beans at runtime

How Jmx bean of Hikari cp can be used in spring boot when boot autoconfigure pooling

How Jmx bean of Hikari cp can be used in spring boot when boot autoconfigure pooling?
i tried following this instruction in below link.
https://github.com/brettwooldridge/HikariCP/wiki/MBean-(JMX)-Monitoring-and-Management.
Hre is my mbean class
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
#ManagedResource(
objectName="PD:category=MBeans,name=testBean",
description="Managed Bean")
#Component("testMbean")
public class HikariJmx {
private String message = "Simple Message";
private int size=0;
public HikariJmx(){
System.out.println("......TestMbean........");
}
#ManagedOperation
public void resetMessageViaMBean(){
this.message = "Message RESET";
}
#ManagedAttribute
public int getSize() throws Exception{
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)");
HikariPoolMXBean poolProxy = JMX.newMXBeanProxy(mBeanServer, poolName, HikariPoolMXBean.class);
return poolProxy.getIdleConnections();
}catch(Exception e){e.printStackTrace();}
return 0;
}
}
Iam able to see the bean in visual vm. But when i take the attribute SIZE i am getting instanceNotFound exception in below line.
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (HikariPool-1)");
I am not creating datasource manually, just gave the properties in the application.properties and boot do the rest.
I had the same problem.
You can get the HikariPoolMXBean from the HikariDataSource itself.
In order to do that, I injected the HikariDataSource to the class I needed the HikariPoolMXBean, and called this method :
HikariPoolMXBean poolProxy = hikariDataSource.getHikariPoolMXBean();

keystore.jks and truststore.jks can't be found in Spring Boot App

I wrote a 2-way authetication restful service cleint to consume a secured restful web service on port 8443 over https. Here are parts of application.properties:
trust.store=classpath:truststore.jks
trust.store.password=xyz123
key.store=classpath:keystore.jks
key.store.password=xyz123
Below are two ways to configure RestTemplate
#Configuration
public class SSLTemplate {
#Value("${key.store}")
private String keyStore;
#Value("${key.store.password}")
private String keyStorePassword;
#Value("${trust.store}")
private String trustStore;
#Value("${trust.store.password}")
private String trustStorePassword;
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {
SSLContext sslContext = SSLContextBuilder.create()
.loadKeyMaterial(ResourceUtils.getFile(keyStore), keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
.loadTrustMaterial(ResourceUtils.getFile(trustStoreF), trustStorePassword.toCharArray())
.build();
...
}
#Configuration
public class SSLTemplate {
#Value("${key.store}")
private Resource keyStoreR;
#Value("${key.store.password}")
private String keyStorePassword;
#Value("${trust.store}")
private Resource trustStoreR;
#Value("${trust.store.password}")
private String trustStorePassword;
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {
SSLContext sslContext = SSLContextBuilder.create()
.loadKeyMaterial(keyStoreR.getURL(), keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
.loadTrustMaterial(trustStoreR.getURL(), trustStorePassword.toCharArray())
.build();
...
}
When I run the app via bootRun or within Eclipse, both of them work.
But when I use jar launcer
java -jar app.jar
I got below exception.
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiation
Exception: Failed to instantiate [org.springframework.web.client.RestTemplate]: Factory method 'restTemplate' threw exception;
nested exception is java.io.FileNotFoundException: URL cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/C:/build/libs/app.jar!/BOOT-INF/classes!/truststore.jks
I have also tried
java -Djavax.net.ssl.trustStore=truststore.jks -Djavax.net.ssl.trustStorePassword=xyz123 -Djavax.net.ssl.keyStore=keystore.jks -Djavax.net.ssl.keyStorePassword=xyz123 -jar app.jar
and got the same exception. Any help will be really appreciated.
Try using resource.getInputStream() instead of resource.getFile() as, resource.getFile() in Spring tries to access a file system path but it can not access a path in your JAR
This link has a rich content and do take a look at Andy's answer here
Sample example:
Resource resource = resourceLoader.getResource("classpath:GeoLite2-Country.mmdb");
InputStream dbAsStream = resource.getInputStream();
and do you have use full path
-Djavax.net.ssl.trustStore=something/like/this/truststore.jks

Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'request' is not active for the current thread for feign client

I am calling another microservice once my current microservice is up and ready using feign client in my current microservice built using Jhipster.
So my Feign Interface is
package com.persistent.integration.client;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.persistent.integration.service.dto.DataPipelineDTO;
#AuthorizedFeignClient(name = "Integrationconfiguration")
public interface DataPipelinesResourceFeign {
#RequestMapping(value = "/api/data-pipelines", method = RequestMethod.GET)
List<DataPipelineDTO> getAllDataPipelines(#RequestParam(value = "pageable") Pageable pageable );
}
}
And I have implemented ApplicationRunner where I have called feign client method.
#Component
public class ApplicationInitializer implements ApplicationRunner {
#Autowired
private DataPipelinesResourceFeign dataPipelinesResourceFeign;
#Autowired
private ActiveMQListener activeMqListener;
#Override
public void run(ApplicationArguments args) throws Exception {
// TODO Auto-generated method stub
Pageable pageable = PageRequest.of(0, 20);
try {
List <DataPipelineDTO> allStartedDataPipeLines = dataPipelinesResourceFeign.getAllDataPipelines(pageable); //.stream().filter(p->p.getState().equals(State.STARTED)).collect(Collectors.toList());
allStartedDataPipeLines.forEach(datapipe ->
{
try {
activeMqListener.consume(datapipe);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
But after running this, it gives below exception at dataPipelinesResourceFeign.getAllDataPipelines :
com.netflix.hystrix.exception.HystrixRuntimeException: DataPipelinesResourceFeign#getAllDataPipelines(Pageable) failed and no fallback available.
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819)
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804)
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1472)
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'scopedTarget.oauth2ClientContext':
Scope 'request' is not active for the current thread; consider
defining a scoped proxy for this bean if you intend to refer to it
from a singleton; nested exception is java.lang.IllegalStateException:
No thread-bound request found: Are you referring to request attributes
outside of an actual web request, or processing a request outside of
the originally receiving thread? If you are actually operating within
a web request and still receive this message, your code is probably
running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the
current request. at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstrac>tBeanFactory.java:362)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractB>eanFactory.java:199)
at
org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTarge>tSource.java:35)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.>java:193)
at com.sun.proxy.$Proxy147.getAccessToken(Unknown Source) at
com.persistent.integration.security.oauth2.AuthorizationHeaderUtil.getAuthoriza>tionHeaderFromOAuth2Context(AuthorizationHeaderUtil.java:28)
at
com.persistent.integration.client.TokenRelayRequestInterceptor.apply(TokenRelay>RequestInterceptor.java:23)
at
feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:158)
at
feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:88)
at
feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
at
feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298)
at
rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
... 68 more Caused by: java.lang.IllegalStateException: No
thread-bound request found: Are you referring to request attributes
outside of an actual web request, or processing a request outside of
the originally receiving thread? If you are actually operating within
a web request and still receive this message, your code is probably
running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the
current request. at
org.springframework.web.context.request.RequestContextHolder.currentRequestAttr>ibutes(RequestContextHolder.java:131)
at
org.springframework.web.context.request.AbstractRequestAttributesScope.get(Abst>ractRequestAttributesScope.java:42)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstrac>tBeanFactory.java:350)
many suggestions on internet were to add listerner RequestContextListener. But problem persisted even if I added listener in webConfigurer.java in onStartup method.
{
servletContext.addListener(RequestContextListener.class);
}
But of no use.
Any leads would be appreciated.
I found a workaround for this. I don't know why TokenRelayRequestIntercepton isn't working but you can use your own RequestInterceptor based on Spring's SecurityContext.
First, define a RequestInterceptor :
public class MyRequestInterceptor implements RequestInterceptor {
public static final String AUTHORIZATION = "Authorization";
public static final String BEARER = "Bearer";
public MyRequestInterceptor() {
super();
}
#Override
public void apply(RequestTemplate template) {
// demander un token à keycloak et le joindre à la request
Optional<String> header = getAuthorizationHeader();
if (header.isPresent()) {
template.header(AUTHORIZATION, header.get());
}
}
public static Optional<String> getAuthorizationHeader() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getDetails() != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oAuth2AuthenticationDetails =
(OAuth2AuthenticationDetails) authentication.getDetails();
return Optional.of(String.format("%s %s", oAuth2AuthenticationDetails.getTokenType(),
oAuth2AuthenticationDetails.getTokenValue()));
} else {
return Optional.empty();
}
}
}
and then, declare a config class for your feign client using your RequestInterceptor, it should contains something like this :
#Bean(name = "myRequestInterceptor")
public RequestInterceptor getMyRequestInterceptor() throws IOException {
return new MyRequestInterceptor();
}
Your Feign client shoud look like this:
#FeignClient(name = "SERVICE_NAME", configuration = MyFeignConfiguration.class)
public interface MyRestClient {
I had the same issue with Feign Client running on startup using ApplicationRunner and I came up with following solution.
I defined my FeignClientsConfiguration with OAuth2FeignRequestInterceptor, which accepts predefined bean DefaultOAuth2ClientContext and OAuth2 configuration OAuth2ProtectedResourceDetails:
#Configuration
public class MyConfig extends FeignClientsConfiguration {
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor( DefaultOAuth2ClientContext oAuth2ClientContext, MyOauth2Properties properties) {
return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resourceDetails(properties));
}
#Bean
public DefaultOAuth2ClientContext oAuth2ClientContext() {
return new DefaultOAuth2ClientContext();
}
private OAuth2ProtectedResourceDetails resourceDetails(MyOauth2Properties oauth2Properties) {
ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
resourceDetails.setAccessTokenUri(oauth2Properties.getAccessTokenUri());
resourceDetails.setUsername(oauth2Properties.getUsername());
resourceDetails.setPassword(oauth2Properties.getPassword());
resourceDetails.setClientId(oauth2Properties.getClientId());
return resourceDetails;
}
}
Your feign client will look something like this:
#FeignClient(url = "http://localhost:8080/api/v1")
public interface FeignClient {
}
After all this, calling FeignClient from ApplicationRunner.run() works fine.
Spring Boot 2.2.6

NoUniqueBeanDefinitionException in Spring annotation driven configuration

I am getting the following error when trying to autowire two beans using
No qualifying bean of type [javax.jms.ConnectionFactory] is defined:
expected single matching bean but found 2: aConnectionFactory, bConnectionFactory
Description:
Parameter 1 of method jmsListenerContainerFactory in org.springframework.boot.autoconfigure.jms.JmsAnnotationDrivenConfiguration required a single bean, but 2 were found:
- aConnectionFactory: defined by method 'aConnectionFactory' in package.Application
- bConnectionFactory: defined by method 'bConnectionFactory' in package.Application
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
I have this annotation driven configuration:
#SpringBootApplication
#EnableIntegration
#IntegrationComponentScan
public class Application extends SpringBootServletInitializer implements
WebApplicationInitializer {
#Resource(name = "aConnectionFactory")
private ConnectionFactory aConnectionFactory;
#Resource(name = "bConnectionFactory")
private ConnectionFactory bConnectionFactory;
#Bean
public IntegrationFlow jmsInboundFlow() {
return IntegrationFlows
.from(
Jms.inboundAdapter(aConnectionFactory)
.destination(aQueue),
e -> e.poller( Pollers.fixedRate(100,
TimeUnit.MILLISECONDS).maxMessagesPerPoll(100))
).channel("entrypoint")
.get();
}
#Bean
public IntegrationFlow jmsInboundFlowB() {
return IntegrationFlows
.from(
Jms.inboundAdapter(bConnectionFactory)
.destination(bQueue),
e -> e.poller( Pollers.fixedRate(100,
TimeUnit.MILLISECONDS).maxMessagesPerPoll(100))
).channel("entrypoint")
.get();
}
#Bean(name = "aConnectionFactory")
#Profile({"weblogic"})
public ConnectionFactory aConnectionFactory() {
ConnectionFactory factory = null;
JndiTemplate jndi = new JndiTemplate();
try {
factory = (ConnectionFactory) jndi.lookup("jms/ConnectionFactory");
} catch (NamingException e) {
logger.error("NamingException for jms/ConnectionFactory", e);
}
return factory;
}
#Bean(name = "bConnectionFactory")
#Profile({"weblogic"})
public ConnectionFactory bConnectionFactory() {
ConnectionFactory factory = null;
JndiTemplate jndi = new JndiTemplate();
try {
factory = (ConnectionFactory) jndi.lookup("jms/ConnectionFactory");
} catch (NamingException e) {
logger.error("NamingException for jms/ConnectionFactory", e);
}
return factory;
}
}
Any ideas what's wrong in this code? This seems to be straight forward, but specifying the Qualifier doesn't work, I have also tried to use #Resource. What am I missing there?
Any help appreciated.
Nothing wrong with your code.
That is just JmsAnnotationDrivenConfiguration from Spring Boot which doesn't like your two ConnectionFactory beans, but requires only one.
Why just don't follow with that report recommendations and mark one of them with the #Primary?
Looks like you don't use Spring Boot JMS auto-configuration feature, so that would be just straightforward to disable JmsAnnotationDrivenConfiguration: http://docs.spring.io/spring-boot/docs/1.4.1.RELEASE/reference/htmlsingle/#using-boot-disabling-specific-auto-configuration
The problem consist
javax.jms.ConnectionFactory is singleton, you need one object that type!
Solutions for your problem:
If you need two object that create objects and extend ConnectionFactory
them change scope as needed.
try #Scope("singleton") or #Scope("prototype").
if you receive error, make a objects. then use a scope #Scope("singleton")
"Other Two" disfigure the other class that is already using and setting such an.

Resources