I am trying to use CDI, using #Inject for dependency injection but my object stays null and won't initialize... more precisely:
I have a webapplication with WeatherController which use a java application with all my modules. In the Java application I have a ForecastService where I try to initialize my repositories with CDI without success.
I tried/searched a lot. Hopefully somebody can help me here?
I have a web application which uses this controller:
#Path("/weather")
public class WeatherController {
private ForecastService forecastService;
//private ForecastRepository forecastRepository = new ForecastFakeDB();
//private ObservationRepository observationRepository = new ObservationFakeDB();
public WeatherController() {
//this.forecastService.setForecastRepository(forecastRepository);
//forecastService.setObservationRepository(observationRepository);
forecastService = new ForecastService();
}
//localhost:8080/DA_project_weatherPredictions/api/weather/observation/Leuven
#GET
#Produces({"application/json"})
#Path("/observation/{location}")
public Response getObservation(#PathParam("location") String location) {
try {
ObjectMapper mapper = new ObjectMapper();
Observation observation = forecastService.getCurrentObservation(location);
//Object to JSON in String
String jsonInString = mapper.writeValueAsString(observation);
return Response.status(200).entity(jsonInString).build();
} catch (Exception ex) {
System.out.println("error");
System.out.println(ex.getMessage());
ex.printStackTrace();
return null;
}
}
This works perfectly. This is my forecastService:
public class ForecastService implements Service {
#Inject
ForecastRepository forecastRepository;
#Inject
ObservationRepository observationRepository;
private Client client;
private WebTarget webTargetObservation, webTargetForecast;
public ForecastService() {
// WeatherRepositoryFactory weatherRepositoryFactory = new WeatherRepositoryFactory();
// forecastRepository = weatherRepositoryFactory.getForecastRepository(repository);
// observationRepository = weatherRepositoryFactory.getObservationRepository(repository);
loadWeather();
}
public void setForecastRepository(ForecastRepository forecastRepository) {
this.forecastRepository = forecastRepository;
}
public void setObservationRepository(ObservationRepository observationRepository) {
this.observationRepository = observationRepository;
}
public void loadWeather() {
//http://api.openweathermap.org/data/2.5/weather?units=metric&appid=12fa8f41738b72d954b6758d48e129aa&q=BE,Leuven
//http://api.openweathermap.org/data/2.5/forecast?units=metric&appid=12fa8f41738b72d954b6758d48e129aa&q=BE,Leuven
client = ClientBuilder.newClient();
webTargetObservation = client.target("http://api.openweathermap.org/data/2.5/weather")
.queryParam("mode", "json")
.queryParam("units", "metric")
.queryParam("appid", "12fa8f41738b72d954b6758d48e129aa");
webTargetForecast = client.target("http://api.openweathermap.org/data/2.5/forecast")
.queryParam("mode", "json")
.queryParam("units", "metric")
.queryParam("appid", "12fa8f41738b72d954b6758d48e129aa");
}
public Observation getCurrentObservation(String location) throws Exception {
Observation observation;
observation = observationRepository.getObservation(location);
if (observation == null) {
try {
//observation = webTargetObservation.queryParam("q", location).request(MediaType.APPLICATION_JSON).get(Observation.class);
Response response = webTargetObservation.queryParam("q", location).request(MediaType.APPLICATION_JSON).get();
String json = response.readEntity(String.class);
//System.out.println(json);
response.close();
observation = new ObjectMapper().readValue(json, Observation.class);
//System.out.println(observation.getWeather().getDescription());
}
catch (Exception e){
StringBuilder sb = new StringBuilder(e.toString());
for (StackTraceElement ste : e.getStackTrace()) {
sb.append("\n\tat ");
sb.append(ste);
}
String trace = sb.toString();
throw new Exception (trace);
//throw new Exception("Location not found");
}
this.observationRepository.addObservation(observation, location);
}
return observation;
}
So the problem is that my repositories stay null
#Alternative
public class ObservationDB implements ObservationRepository{
//as ID we can use the ASCI value of the String key .. example uklondon to ASCII
public ObservationDB(String name) {
}
#Override
public Observation getObservation(String location) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void addObservation(Observation observation, String location) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Mermory DB:
#Default
public class ObservationFakeDB implements ObservationRepository {
//example String key : beleuven, uklondon
private static Map<String, Observation> observations;
public ObservationFakeDB() {
observations = new HashMap<>();
}
#Override
public Observation getObservation(String location) {
return observations.get(location);
}
#Override
public void addObservation(Observation observation, String location) {
observations.put(location, observation);
}
}
I have a beans.xml, I thought beans.xml, #Inject, #Default en #Alternative would make this work. I tried #Dependent, #Applicationscoped.
EDIT:
I also often get this warning on Netbeans.
My beans.xml
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
You need to let your CDI container manages the lifecycle of all your beans to allow it to resolve and inject properly their dependencies.
So, in your case you should not create yourself the instance of ForecastService, you should rather delegate it to the CDI container by simply annotating the field forecastService with #Inject this way its dependencies will be automatically resolved and set by the container.
public class WeatherController {
#Inject
private ForecastService forecastService;
...
Related
I need to validate my ui data and api responses are same,
here is my code I tried,
private ValidateContentPage cp = new ValidateContentPage();
public void getTitle() {
String UITitle = driver.findElement(titlepage).getText();
System.out.println(UITitle);
Assert.assertEquals(UITitle, cp.getAPICall(),"Passed");
}
here im getting my api responses,
public class ValidateContentPage {
public common cm = new common();
public Properties prop;
public void baseURI() {
prop = cm.getProperties("./src/test/API/IndiaOne/propertyfile/EndpointURL.properties");
RestAssured.baseURI = prop.getProperty("baseURI");
}
public String getAPICall() {
objectpojo ps = given().expect().defaultParser(Parser.JSON).when().get(prop.getProperty("resources")).as(objectpojo.class, cm.getMapper());
int number = ps.getPosts().size();
System.out.println(number);
System.out.println(ps.getPosts().get(0).getTitle());
return ps.getPosts().get(0).getTitle();
}
If i validate both using testng assertion it throwing null pointer exception, anyone help me on how to validate my ui data and api responses.
You need to call your ValidateContentPage from #Test itself or from #BeforeTest
#Test
public void getTitle() {
String UITitle = driver.findElement(titlepage).getText();
System.out.println(UITitle);
ValidateContentPage cp = new ValidateContentPage();
Assert.assertEquals(UITitle, cp.getAPICall(),"Passed");
}
Quick background, our company connects to an ERP system (Sage) via web services for some functions. We have both c# (.net) and java code that performs calls to the Web Service (WS). Recently Sage introduced Basic Authentication into their WS.
Please note: This is a JAVA question, but I'll show an example in C# first to explain.
In the c# program, I first would create an object that is for accessing the WS:
var sageService = new CAdxWebServiceXmlCCServiceBasicAuth();
I then set up credential information:
var sageServiceCallContext = SageFactory.Instance.GetCallContext();
sageService.Credentials = new NetworkCredential(SageUser, SagePwd);
sageService.PreAuthenticate = true;
then finally the call to the specific web service method:
sageCustomerSvcResponse = sageService.run(sageServiceCallContext, "YTDPROF", sageCustomerRequestInXml);
When I set up the service object I use a custom class that looks like this:
public class CAdxWebServiceXmlCCServiceBasicAuth : CAdxWebServiceXmlCCService
{
protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest webRequest = (HttpWebRequest)base.GetWebRequest(uri);
NetworkCredential credentials = Credentials as NetworkCredential;
if (credentials != null)
{
string authInfo = "";
if (credentials.Domain != null && credentials.Domain.Length > 0)
{
authInfo = string.Format(#"{0}\{1}:{2}", credentials.Domain, credentials.UserName, credentials.Password);
}
else
{
authInfo = string.Format(#"{0}:{1}", credentials.UserName, credentials.Password);
};
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
webRequest.Headers["Authorization"] = "Basic " + authInfo;
}
return webRequest;
}
}
What happens is that now, when I perform any call to the web service methods, the GetWebRequest from the class is invoked every time. This is how we implemented basis authentication in c#.
How do I do this in Java?
In the java code currently, I create the service object (that which accesses the web services) this way:
WebServiceInvoker service = new WebServiceInvoker(SageWSURL,"");
and the WebServiceInvoker looks like this (truncated for brevity):
public WebServiceInvoker(String url, String dummy) throws ServiceException, IOException {
serviceLocator = new CAdxWebServiceXmlCCServiceLocator();
service = serviceLocator.getCAdxWebServiceXmlCC(url);
cc = new CAdxCallContext();
cc.setCodeLang("ENG");
cc.setCodeUser("USER");
cc.setPassword("PAWWORD");
cc.setPoolAlias("POOL");
cc.setRequestConfig("adxwss.trace.on=on&adxwss.trace.size=16384&adonix.trace.on=on&adonix.trace.level=3&adonix.trace.size=8");
log = new PrintWriter(new BufferedWriter(new FileWriter("C:/Kalio/service/orders/log.txt")));
}
the webservice locator looks like this:
public class CAdxWebServiceXmlCCServiceLocator extends org.apache.axis.client.Service implements com.adonix.www.WSS.CAdxWebServiceXmlCCService {
public CAdxWebServiceXmlCCServiceLocator() {
}
public com.adonix.www.WSS.CAdxWebServiceXmlCC getCAdxWebServiceXmlCC() throws javax.xml.rpc.ServiceException {
java.net.URL endpoint;
System.out.println("using local Sage Web Servivce URL:" + CAdxWebServiceXmlCC_address);
try {
endpoint = new java.net.URL(CAdxWebServiceXmlCC_address);
}
catch (java.net.MalformedURLException e) {
throw new javax.xml.rpc.ServiceException(e);
}
return getCAdxWebServiceXmlCC(endpoint);
}
public com.adonix.www.WSS.CAdxWebServiceXmlCC getCAdxWebServiceXmlCC(java.net.URL portAddress) throws javax.xml.rpc.ServiceException {
try {
com.adonix.www.WSS.CAdxWebServiceXmlCCSoapBindingStub _stub = new com.adonix.www.WSS.CAdxWebServiceXmlCCSoapBindingStub(portAddress, this);
_stub.setPortName(getCAdxWebServiceXmlCCWSDDServiceName());
return _stub;
}
catch (org.apache.axis.AxisFault e) {
return null;
}
}
and the specific method within that class is this:
public String getCustomer(String constructedXML) throws RemoteException {
**CAdxResultXml result = service.run(cc, "XTDPROF", constructedXML);**
CAdxMessage[] messages = result.getMessages();
for (int i = 0; i<messages.length; i++) {
CAdxMessage message = messages[i];
log.println("X3 get customer message: " + message.getMessage());
log.println("X3 get customer message type: " + message.getType());
}
return result.getResultXml();
}
So my questions is, how to I emulate that override that I see in the .net program in Java? It seems like it would be somewhere in either the service locator or invoker, but the program does not use standard http classes, but this adxwss stuff. I tried a straight c# to java conversion and that way didn't help. I have seen examples where basicAuth is implemented, but not against web service calls.
The c# is pretty clear cut, because once I create the service object using the basicAuth override, every web service calls goes through the orderride. How does that happen in Java?
I'll be happy to provide more info if needed and I'll continue to look/experiment, but at the moment I'm treading water.
Adding:
In tracing through the java code I found the specific web service call (run) where an apache "call" object is created. Is this where basicauth can be added?
public com.adonix.www.WSS.CAdxResultXml run(com.adonix.www.WSS.CAdxCallContext callContext, java.lang.String publicName, java.lang.String inputXml) throws java.rmi.RemoteException {
if (super.cachedEndpoint == null) {
throw new org.apache.axis.NoEndPointException();
}
org.apache.axis.client.Call _call = createCall();
_call.setOperation(_operations[0]);
_call.setUseSOAPAction(true);
_call.setSOAPActionURI("");
_call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOAP11_CONSTANTS);
_call.setOperationName(new javax.xml.namespace.QName("http://www.adonix.com/WSS", "run"));
setRequestHeaders(_call);
setAttachments(_call);
try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {callContext, publicName, inputXml});
if (_resp instanceof java.rmi.RemoteException) {
throw (java.rmi.RemoteException)_resp;
}
else {
extractAttachments(_call);
try {
return (com.adonix.www.WSS.CAdxResultXml) _resp;
} catch (java.lang.Exception _exception) {
return (com.adonix.www.WSS.CAdxResultXml) org.apache.axis.utils.JavaUtils.convert(_resp, com.adonix.www.WSS.CAdxResultXml.class);
}
}
} catch (org.apache.axis.AxisFault axisFaultException) {
throw axisFaultException;
}
}
The solution I came up with is not elegant, but then I'm not a guru in Java, just know enough to be given these tasks.
Our company uses Sage as our ERP system and Sage has a WSDL to define the basic web services it provides.
Sage Web Servicew WSDL
In their latest version of Sage they went with basic authentication, but did not build it into the new WSDL. Since I could not seem to extend the class (CAdxWebServiceXmlCCService), I just copied/pasted a new class called CAdxWebServiceXmlCCServiceBasicAuth. The full code is shown below if anyone ever has need to deal with something like this in a web service.
The key point where BaiscAuth set set up is in the getCAdxWebServiceXmlCC method. I added setPassword and setUserName to the stub that is returned. What this accomplishes is that every time I perform a webservice method call, that stub is now part of the header.
package com.adonix.www.WSS;
import java.net.URL;
import java.util.Base64;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import javax.xml.rpc.ServiceException;
public class CAdxWebServiceXmlCCServiceBasicAuth extends org.apache.axis.client.Service implements com.adonix.www.WSS.CAdxWebServiceXmlCCService {
public CAdxWebServiceXmlCCServiceBasicAuth() {
}
public CAdxWebServiceXmlCCServiceBasicAuth(org.apache.axis.EngineConfiguration config) {
super(config);
}
public CAdxWebServiceXmlCCServiceBasicAuth(java.lang.String wsdlLoc, javax.xml.namespace.QName sName) throws javax.xml.rpc.ServiceException {
super(wsdlLoc, sName);
}
// Use to get a proxy class for CAdxWebServiceXmlCC
private java.lang.String CAdxWebServiceXmlCC_address = "http://10.28.0.7:8124/soap-generic/syracuse/collaboration/syracuse/CAdxWebServiceXmlCC";
public java.lang.String getCAdxWebServiceXmlCCAddress() {
return CAdxWebServiceXmlCC_address;
}
// The WSDD service name defaults to the port name.
private java.lang.String CAdxWebServiceXmlCCWSDDServiceName = "CAdxWebServiceXmlCC";
public java.lang.String getCAdxWebServiceXmlCCWSDDServiceName() {
return CAdxWebServiceXmlCCWSDDServiceName;
}
public void setCAdxWebServiceXmlCCWSDDServiceName(java.lang.String name) {
CAdxWebServiceXmlCCWSDDServiceName = name;
}
public com.adonix.www.WSS.CAdxWebServiceXmlCC getCAdxWebServiceXmlCC(String userName,String password) throws javax.xml.rpc.ServiceException {
java.net.URL endpoint;
try {
endpoint = new java.net.URL(CAdxWebServiceXmlCC_address);
}
catch (java.net.MalformedURLException e) {
throw new javax.xml.rpc.ServiceException(e);
}
return getCAdxWebServiceXmlCC(endpoint,userName,password);
}
public com.adonix.www.WSS.CAdxWebServiceXmlCC getCAdxWebServiceXmlCC(java.net.URL portAddress,String userName,String password) throws javax.xml.rpc.ServiceException {
try {
com.adonix.www.WSS.CAdxWebServiceXmlCCSoapBindingStub _stub = new com.adonix.www.WSS.CAdxWebServiceXmlCCSoapBindingStub(portAddress, this);
_stub.setPortName(getCAdxWebServiceXmlCCWSDDServiceName());
_stub.setPassword(password);
_stub.setUsername(userName);
return _stub;
}
catch (org.apache.axis.AxisFault e) {
return null;
}
}
public void setCAdxWebServiceXmlCCEndpointAddress(java.lang.String address) {
CAdxWebServiceXmlCC_address = address;
}
/**
* For the given interface, get the stub implementation.
* If this service has no port for the given interface,
* then ServiceException is thrown.
*/
public java.rmi.Remote getPort(Class serviceEndpointInterface) throws javax.xml.rpc.ServiceException {
try {
if (com.adonix.www.WSS.CAdxWebServiceXmlCC.class.isAssignableFrom(serviceEndpointInterface)) {
com.adonix.www.WSS.CAdxWebServiceXmlCCSoapBindingStub _stub = new com.adonix.www.WSS.CAdxWebServiceXmlCCSoapBindingStub(new java.net.URL(CAdxWebServiceXmlCC_address), this);
_stub.setPortName(getCAdxWebServiceXmlCCWSDDServiceName());
return _stub;
}
}
catch (java.lang.Throwable t) {
throw new javax.xml.rpc.ServiceException(t);
}
throw new javax.xml.rpc.ServiceException("There is no stub implementation for the interface: " + (serviceEndpointInterface == null ? "null" : serviceEndpointInterface.getName()));
}
/**
* For the given interface, get the stub implementation.
* If this service has no port for the given interface,
* then ServiceException is thrown.
*/
public java.rmi.Remote getPort(javax.xml.namespace.QName portName, Class serviceEndpointInterface) throws javax.xml.rpc.ServiceException {
if (portName == null) {
return getPort(serviceEndpointInterface);
}
java.lang.String inputPortName = portName.getLocalPart();
if ("CAdxWebServiceXmlCC".equals(inputPortName)) {
return getCAdxWebServiceXmlCC();
}
else {
java.rmi.Remote _stub = getPort(serviceEndpointInterface);
((org.apache.axis.client.Stub) _stub).setPortName(portName);
return _stub;
}
}
public javax.xml.namespace.QName getServiceName() {
return new javax.xml.namespace.QName("http://www.adonix.com/WSS", "CAdxWebServiceXmlCCService");
}
private java.util.HashSet ports = null;
public java.util.Iterator getPorts() {
if (ports == null) {
ports = new java.util.HashSet();
ports.add(new javax.xml.namespace.QName("http://www.adonix.com/WSS", "CAdxWebServiceXmlCC"));
}
return ports.iterator();
}
/**
* Set the endpoint address for the specified port name.
*/
public void setEndpointAddress(java.lang.String portName, java.lang.String address) throws javax.xml.rpc.ServiceException {
if ("CAdxWebServiceXmlCC".equals(portName)) {
setCAdxWebServiceXmlCCEndpointAddress(address);
}
else
{ // Unknown Port Name
throw new javax.xml.rpc.ServiceException(" Cannot set Endpoint Address for Unknown Port" + portName);
}
}
/**
* Set the endpoint address for the specified port name.
*/
public void setEndpointAddress(javax.xml.namespace.QName portName, java.lang.String address) throws javax.xml.rpc.ServiceException {
setEndpointAddress(portName.getLocalPart(), address);
}
#Override
public CAdxWebServiceXmlCC getCAdxWebServiceXmlCC() throws ServiceException {
// TODO Auto-generated method stub
return null;
}
#Override
public CAdxWebServiceXmlCC getCAdxWebServiceXmlCC(URL portAddress) throws ServiceException {
// TODO Auto-generated method stub
return null;
}
}
I am trying to override Ribbon Server List to get a list of host names from consul. I have the consul piece working properly(when testing with hardcode values) to get the hostname and port for a service. The issue I am having is when I try to autowire in IClientConfig. I get an exception that IClientConfig bean could not be found. How do I override the ribbon configurations and autowire IClientConfig in the ribbonServerList method.
I have tried following the instructions here at http://projects.spring.io/spring-cloud/spring-cloud.html#_customizing_the_ribbon_client on how to customize ribbon client configuration. I keep getting the following error:
Description:
Parameter 0 of method ribbonServerList in com.intradiem.enterprise.keycloak.config.ConsulRibbonSSLConfig required a bean of type 'com.netflix.client.config.IClientConfig' that could not be found.
Which is causing spring-boot to fail.
Bellow are the classes that I am trying to use to create
AutoConfiguration Class:
#Configuration
#EnableConfigurationProperties
#ConditionalOnBean(SpringClientFactory.class)
#ConditionalOnProperty(value = "spring.cloud.com.intradiem.service.apirouter.consul.ribbon.enabled", matchIfMissing = true)
#AutoConfigureAfter(RibbonAutoConfiguration.class)
#RibbonClients(defaultConfiguration = ConsulRibbonSSLConfig.class)
//#RibbonClient(name = "question-answer-provider", configuration = ConsulRibbonSSLConfig.class)
public class ConsulRibbonSSLAutoConfig
{
}
Configuration Class:
#Component
public class ConsulRibbonSSLConfig
{
#Autowired
private ConsulClient client;
private String serviceId = "client";
public ConsulRibbonSSLConfig() {
}
public ConsulRibbonSSLConfig(String serviceId) {
this.serviceId = serviceId;
}
#Bean
#ConditionalOnMissingBean
public ServerList<?> ribbonServerList(IClientConfig clientConfig) {
ConsulSSLServerList serverList = new ConsulSSLServerList(client);
serverList.initWithNiwsConfig(clientConfig);
return serverList;
}
}
ServerList Code:
public class ConsulSSLServerList extends AbstractServerList<Server>
{
private final ConsulClient client;
private String serviceId = "client";
public ConsulSSLServerList(ConsulClient client) {
this.client = client;
}
#Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
this.serviceId = clientConfig.getClientName();
}
#Override
public List<Server> getInitialListOfServers() {
return getServers();
}
#Override
public List<Server> getUpdatedListOfServers() {
return getServers();
}
private List<Server> getServers() {
List<Server> servers = new ArrayList<>();
Response<QueryExecution> results = client.executePreparedQuery(serviceId, QueryParams.DEFAULT);
List<QueryNode> nodes = results.getValue().getNodes();
for (QueryNode queryNode : nodes) {
QueryNode.Node node = queryNode.getNode();
servers.add(new Server(node.getMeta().containsKey("secure") ? "https" : "http", node.getNode(), queryNode.getService().getPort()));
}
return servers;
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder("ConsulSSLServerList{");
sb.append("serviceId='").append(serviceId).append('\'');
sb.append('}');
return sb.toString();
}
}
In my controller, I receive a string parameter on the basis of which I need to decide which service to call, how can I do the same in my Spring Boot application using Spring annotations?
For example: we have different types of cars. Now, on the basis of parameter in the request I should be able to decide which particular car service I should call.
How can I have a factory using annotations in Spring Boot, and objects should be returned from that factory on the basis of input.
I remember implementing support for this approach a few years ago, I believe inspired and using https://www.captechconsulting.com/blogs/combining-strategy-pattern-and-spring as the entry point to my utility library, use the following code snippets at your convenience:
Strategy.java
package ...
#Documented
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface Strategy {
Class<?> type();
String[] profiles() default {};
}
StrategyFactory.java
package ...
public class StrategyFactory {
private static final Logger LOG = Logger.getLogger( StrategyFactory.class );
private Map<Class<?>, Strategy> strategiesCache = new HashMap<Class<?>, Strategy>();
private String[] packages;
#PostConstruct
public void init() {
if (this.packages != null) {
Set<Class<?>> annotatedClasses = new HashSet<Class<?>>();
for (String pack : this.packages) {
Reflections reflections = new Reflections( pack );
annotatedClasses.addAll( reflections.getTypesAnnotatedWith( Strategy.class ) );
}
this.sanityCheck( annotatedClasses );
}
}
public <T> T getStrategy(Class<T> strategyClass) {
return this.getStrategy( strategyClass, null );
}
#SuppressWarnings("unchecked")
public <T> T getStrategy(Class<T> strategyClass, String currentProfile) {
Class<T> clazz = (Class<T>) this.findStrategyMatchingProfile( strategyClass, currentProfile );
if (clazz == null) {
throw new StrategyNotFoundException( String.format( "No strategies found of type '%s', are the strategies marked with #Strategy?", strategyClass.getName() ) );
}
try {
return (T) clazz.newInstance();
} catch (Exception e) {
throw ExceptionUtils.rethrowAs( e, StrategyException.class );
}
}
/**
* Checks to make sure there is only one strategy of each type(Interface) annotated for each profile Will throw an exception on startup if multiple strategies are mapped to the same profile.
* #param annotatedClasses a list of classes
*/
private void sanityCheck(Set<Class<?>> annotatedClasses) {
Set<String> usedStrategies = new HashSet<String>();
for (Class<?> annotatedClass : annotatedClasses) {
Strategy strategyAnnotation = AnnotationUtils.findAnnotation( annotatedClass, Strategy.class );
if (!strategyAnnotation.type().isAssignableFrom( annotatedClass )) {
throw new StrategyProfileViolationException( String.format( "'%s' should be assignable from '%s'", strategyAnnotation.type(), annotatedClass ) );
}
this.strategiesCache.put( annotatedClass, strategyAnnotation );
if (this.isDefault( strategyAnnotation )) {
this.ifNotExistAdd( strategyAnnotation.type(), "default", usedStrategies );
} else {
for (String profile : strategyAnnotation.profiles()) {
this.ifNotExistAdd( strategyAnnotation.type(), profile, usedStrategies );
}
}
}
}
private void ifNotExistAdd(Class<?> type, String profile, Set<String> usedStrategies) {
String key = this.createKey( type, profile );
if (usedStrategies.contains( key )) {
throw new StrategyProfileViolationException( String.format( "There can only be a single strategy for each type, found multiple for type '%s' and profile '%s'", type, profile ) );
}
usedStrategies.add( key );
}
private String createKey(Class<?> type, String profile) {
return String.format( "%s_%s", type, profile ).toLowerCase();
}
private boolean isDefault(Strategy strategyAnnotation) {
return (strategyAnnotation.profiles().length == 0);
}
private Class<?> findStrategyMatchingProfile(Class<?> strategyClass, String currentProfile) {
for (Map.Entry<Class<?>, Strategy> strategyCacheEntry : this.strategiesCache.entrySet()) {
Strategy strategyCacheEntryValue = strategyCacheEntry.getValue();
if (strategyCacheEntryValue.type().equals( strategyClass )) {
if (currentProfile != null) {
for (String profile : strategyCacheEntryValue.profiles()) {
if (currentProfile.equals( profile )) {
Class<?> result = strategyCacheEntry.getKey();
if (LOG.isDebugEnabled()) {
LOG.debug( String.format( "Found strategy [strategy=%s, profile=%s, strategyImpl=%s]", strategyClass, currentProfile, result ) );
}
return result;
}
}
} else if (this.isDefault( strategyCacheEntryValue )) {
Class<?> defaultClass = strategyCacheEntry.getKey();
if (LOG.isDebugEnabled()) {
LOG.debug( String.format( "Found default strategy [strategy=%s, profile=%s, strategyImpl=%s]", strategyClass, currentProfile, defaultClass ) );
}
return defaultClass;
}
}
}
return null;
}
public void setPackages(String[] packages) {
this.packages = packages;
}
}
StrategyException.java
package ...
public class StrategyException extends RuntimeException {
...
}
StrategyNotFoundException.java
package ...
public class StrategyNotFoundException extends StrategyException {
...
}
StrategyProfileViolationException.java
package ...
public class StrategyProfileViolationException extends StrategyException {
...
}
Usage without Spring:
NavigationStrategy.java
package com.asimio.core.test.strategy.strategies.navigation;
public interface NavigationStrategy {
public String naviateTo();
}
FreeNavigationStrategy.java
package com.asimio.core.test.strategy.strategies.navigation;
#Strategy(type = NavigationStrategy.class)
public class FreeNavigationStrategy implements NavigationStrategy {
public String naviateTo() {
return "free";
}
}
LimitedPremiumNavigationStrategy.java
package com.asimio.core.test.strategy.strategies.navigation;
#Strategy(type = NavigationStrategy.class, profiles = { "limited", "premium" })
public class LimitedPremiumNavigationStrategy implements NavigationStrategy {
public String naviateTo() {
return "limited+premium";
}
}
Then
...
StrategyFactory factory = new StrategyFactory();
factory.setPackages( new String[] { "com.asimio.core.test.strategy.strategies.navigation" } );
this.factory.init();
NavigationStrategy ns = this.factory.getStrategy( NavigationStrategy.class );
String result = ns.naviateTo();
Assert.assertThat( "free", Matchers.is( result ) );
...
Or
...
String result = factory.getStrategy( NavigationStrategy.class, "limited" ).naviateTo();
Assert.assertThat( "limited+premium", Matchers.is( result ) );
...
Usage with Spring:
Spring context file:
<bean id="strategyFactory" class="com.asimio.core.strategy.StrategyFactory">
<property name="packages">
<list>
<value>com.asimio.jobs.feed.impl</value>
</list>
</property>
</bean>
IFeedProcessor.java
package ...
public interface IFeedProcessor {
void runBatch(String file);
}
CsvRentalsFeedProcessor.java
package ...
#Configurable(dependencyCheck = true)
#Strategy(type = IFeedProcessor.class, profiles = { "csv" })
public class CsvRentalsFeedProcessor implements IFeedProcessor, Serializable {
#Autowired
private CsvRentalsBatchReporter batchReporter;
...
}
Then
...
IFeedProcessor feedProcessor = this.strategyFactory.getStrategy( IFeedProcessor.class, feedFileExt );
feedProcessor.runBatch( unzippedFeedDir.getAbsolutePath() + File.separatorChar + feedFileName );
...
Notice CsvRentalsBatchReporter is "injected" in CsvRentalsFeedProcessor bean (a Strategy implementation) but StrategyFactory instantiates the strategy implementation using return (T) clazz.newInstance();, so what's needed to make this object Spring-aware?
First CsvRentalsFeedProcessor to be annotated with #Configurable(dependencyCheck = true) and when running the Java application this argument is needed in the java command: -javaagent:<path to spring-agent-${spring.version}.jar>
<soapenv:Fault xmlns:m="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode>m:Server</faultcode>
<faultstring>someFaultString</faultstring>
<detail>
<NS1:confirmIdentityErrorInfo xmlns:NS1="example.com">
<faultInfo>
<faultType>Business Exception</faultType>
<faultCode>100</faultCode>
<message>some message</message>
<faultState>fault state</faultState>
</faultInfo>
</NS1:confirmIdentityErrorInfo>
</detail>
</soapenv:Fault>
I would need to generate the fault object as above. I am able to generate 'confirmIdentityErrorInfo' but not able to add child elements. I tried this option, but I couldn't generated nested object structure.
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public SoapFaultMappingExceptionResolver exceptionResolver(){
SoapFaultMappingExceptionResolver exceptionResolver = new DetailSoapFaultDefinitionExceptionResolver();
SoapFaultDefinition faultDefinition = new SoapFaultDefinition();
faultDefinition.setFaultCode(SoapFaultDefinition.SERVER);
exceptionResolver.setDefaultFault(faultDefinition);
Properties errorMappings = new Properties();
errorMappings.setProperty(Exception.class.getName(), SoapFaultDefinition.SERVER.toString());
errorMappings.setProperty(BusinessException.class.getName(), SoapFaultDefinition.SERVER.toString());
exceptionResolver.setExceptionMappings(errorMappings);
exceptionResolver.setOrder(1);
return exceptionResolver;
}
}
protected void customizeFault(Object endpoint, Exception ex, SoapFault fault) {
if (ex instanceof BusinessException) {
SoapFaultDetail detail = fault.addFaultDetail();
try
{
QName entryName = new QName("namespace", "confirmIdentityErrorInfo", "NS1");
detail.addFaultDetailElement(entryName);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
But I am not sure how to add child elements to QName. Can someone help me how to product the fault detail as shown above?
This is the solution.
fault.addFaultDetail();
this.marshaller.marshal(((AJAXB2MarshalledBusinessException)ex).getMyJAXB2MarshallableComplexMessage(), fault.getFaultDetail().getResult());
More detailed in this link:
public class DetailSoapFaultDefinitionExceptionResolver extends SoapFaultMappingExceptionResolver {
private static final ObjectFactory FACTORY = new ObjectFactory();
private final Marshaller marshaller;
public DetailSoapFaultDefinitionExceptionResolver() throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance("your.schema");
this.marshaller = jaxbContext.createMarshaller();
}
#Override
protected void customizeFault(Object endpoint, Exception e, SoapFault fault) {
YourFault xsdFault = new YourFault();
xsdFault.setCode("UNKNOWN_FAILURE");
xsdFault.setDescription(e.getMessage());
SoapFaultDetail faultDetail = fault.addFaultDetail();
try {
marshaller.marshal(FACTORY.createSubscriptionManagementFault(xsdFault), faultDetail.getResult());
} catch (JAXBException e1) {
log.error("Could not marshall SubscriptionManagementFault", e);
}
}
Here is detailed explanation
https://maarten.mulders.it/2018/07/custom-soap-faults-using-spring-ws/