EncryptedData was not recognized, apache cxf with spring - spring-boot

my error is this:
org.apache.cxf.interceptor.Fault: Message part
{http://www.w3.org/2001/04/xmlenc#}EncryptedData was not recognized.
(Does it exist in service WSDL?)
Which is due to setting properties for decoding ecrypted data. My issue is that I am having problems how to do that with apache cxf (Timestamp and Signature works ok).
Here is my part of code:
public WSS4JStaxInInterceptor wss4JStaxInInterceptor() throws Exception {
WSSSecurityProperties inProperties = new WSSSecurityProperties();
//inProperties.addAction(WSSConstants.USERNAMETOKEN);
inProperties.addAction(WSSConstants.TIMESTAMP);
inProperties.addAction(WSSConstants.SIGNATURE);
inProperties.addAction(WSSConstants.ENCRYPTION);
inProperties.setEncryptionUser("xxx");
inProperties.loadDecryptionKeystore(this.getClass().getClassLoader().getResource("\"C:\\\\Users\\\\miha_\\\\OneDrive\\\\Dokumenti\\\\Job\\\\Lj\\\\Spring\\\\demo\\\\src\\\\main\\\\resources\\\\xxxx.jks"),"xxx".toCharArray());;
inProperties.setMustUnderstand(false);
inProperties.loadSignatureKeyStore(this.getClass().getClassLoader().getResource("\"C:\\\\Users\\\\miha_\\\\OneDrive\\\\Dokumenti\\\\Job\\\\Lj\\\\Spring\\\\demo\\\\src\\\\main\\\\resources\\\\xxxx.jks"),"xxx".toCharArray());
inProperties.setSignatureUser("cbd");
//inProperties.setSignatureVerificationCryptoProperties(wss4jInProperties());
//inProperties.setUsernameTokenPasswordType(WSSConstants.UsernameTokenPasswordType.PASSWORD_DIGEST);
inProperties.setCallbackHandler(new ClientKeystorePasswordCallback());
WSS4JStaxInInterceptor wss4JStaxInInterceptor = new WSS4JStaxInInterceptor(inProperties);
return wss4JStaxInInterceptor;
}
So I define "loadDecryptionKeystore" in which I get keystore. But where do I define which certificate to take (with setEncryptionUser("xxx"); ?) and where password to access private key in certificate?
Should I define also something else, how ?
ps.: this is configuration for server part when receiving request
thank you

You define which certificate to take by calling setEncryptionUser.
The password for the private key should by supplied by the CallbackHandler that you define by calling setCallbackHandler. When the password for the private key will be needed, the framework will request it by calling the callback handler with an instance of WSPasswordCallback (see the documentation section about WSPasswordCallback identifiers for details).
A simple example of a callback handler:
/**
* #see ClientKeystorePasswordCallback
*/
public class ClientKeystorePasswordCallback implements CallbackHandler {
private Map<String, String> passwords =
new HashMap<String, String>();
public ClientKeystorePasswordCallback() {
passwords.put("myclientkey", "ckpass");
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
String pass = passwords.get(pc.getIdentifier());
if (pass != null) {
pc.setPassword(pass);
return;
}
}
}
}

Related

creating Opentelemetry Context using trace-id and span-id of remote parent

I have micro service which support open tracing and that injecting trace-id and span-id in to header. Other micro service support open telemetry. how can I create parent span using trace-id and span-id in second micro service?
Thanks,
You can use W3C Trace Context specifications to achieve this. We need to send traceparent(Ex: 00-8652a752089f33e2659dff28d683a18f-7359b90f4355cfd9-01) from producer via HTTP headres ( or you can create it using the trace-id and span-id in the consumer). Then we can extract the remote context and create the span with traceparent.
This is the consumer controller. TextMapGetter used to map that traceparent data to the Context. ExtractModel is just a custom class.
#GetMapping(value = "/second")
public String sencondTest(#RequestHeader(value = "traceparent") String traceparent){
try {
Tracer tracer = openTelemetry.getTracer("cloud.events.second");
TextMapGetter<ExtractModel> getter = new TextMapGetter<>() {
#Override
public String get(ExtractModel carrier, String key) {
if (carrier.getHeaders().containsKey(key)) {
return carrier.getHeaders().get(key);
}
return null;
}
#Override
public Iterable<String> keys(ExtractModel carrier) {
return carrier.getHeaders().keySet();
}
};
ExtractModel model = new ExtractModel();
model.addHeader("traceparent", traceparent);
Context extractedContext = openTelemetry.getPropagators().getTextMapPropagator()
.extract(Context.current(), model, getter);
try (Scope scope = extractedContext.makeCurrent()) {
// Automatically use the extracted SpanContext as parent.
Span serverSpan = tracer.spanBuilder("CloudEvents Server")
.setSpanKind(SpanKind.SERVER)
.startSpan();
try {
Thread.sleep(150);
} finally {
serverSpan.end();
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Server Received!";
}
Then when we configuring the OpenTelemetrySdk need to set W3CTraceContextPropagator in Context Propagators.
// Use W3C Propagator(to extract span from HTTP headers) since we use the W3C specifications
TextMapPropagator textMapPropagator = W3CTraceContextPropagator.getInstance();
OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setPropagators(ContextPropagators.create(textMapPropagator))
.buildAndRegisterGlobal();
Here is my customer ExtractModel class
public class ExtractModel {
private Map<String, String> headers;
public void addHeader(String key, String value) {
if (this.headers == null){
headers = new HashMap<>();
}
headers.put(key, value);
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
}
You can find more details in the official documentation for manual instrumentation.
Generally you have to propogate the span-id and trace-id if it is available in header. Any request you get in your microservice, check if the headers have span-id and trace-id in them. If yes,extract them and use them in your service.
If it is not present then you create a new one and use it in your service and also add it to requests that go out of your microservice.

spring: customizing the authorizationEndpoint (OAuth2)

I am trying to customize the code of the spring oauth authorization server.
for now I have just copied the framework authorizationEndpoint code and placed it in another class. I just changed the address mapping to /custom/oauth/authorize. I have also added #Controller before the class declaration otherwise this code will not be used at all:
#Controller
//#Order(Ordered.HIGHEST_PRECEDENCE)
#SessionAttributes("authorizationRequest")
public class AuthorizationEndpointCustom extends AuthorizationEndpoint {
#Autowired
private AuthenticationManager authenticationManager;
private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
private RedirectResolver redirectResolver = new DefaultRedirectResolver();
private UserApprovalHandler userApprovalHandler = new DefaultUserApprovalHandler();
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
private String userApprovalPage = "forward:/oauth/confirm_access";
private String errorPage = "forward:/oauth/error";
private Object implicitLock = new Object();
public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
this.sessionAttributeStore = sessionAttributeStore;
}
public void setErrorPage(String errorPage) {
this.errorPage = errorPage;
}
#RequestMapping(value = "/oauth/authorize")
public ModelAndView authorize(Map<String, Object> model, #RequestParam Map<String, String> parameters,
SessionStatus sessionStatus, Principal principal) {
System.out.println("\n\ninside custom authorization endpoint");
// Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
// query off of the authorization request instead of referring back to the parameters map. The contents of the
// parameters map will be stored without change in the AuthorizationRequest object once it is created.
AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
Set<String> responseTypes = authorizationRequest.getResponseTypes();
if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
}
if (authorizationRequest.getClientId() == null) {
throw new InvalidClientException("A client id must be provided");
}
try {
if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
throw new InsufficientAuthenticationException(
"User must be authenticated with Spring Security before authorization can be completed.");
}
ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
// The resolved redirect URI is either the redirect_uri from the parameters or the one from
// clientDetails. Either way we need to store it on the AuthorizationRequest.
String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
if (!StringUtils.hasText(resolvedRedirect)) {
throw new RedirectMismatchException(
"A redirectUri must be either supplied or preconfigured in the ClientDetails");
}
authorizationRequest.setRedirectUri(resolvedRedirect);
// We intentionally only validate the parameters requested by the client (ignoring any data that may have
// been added to the request by the manager).
oauth2RequestValidator.validateScope(authorizationRequest, client);
// Some systems may allow for approval decisions to be remembered or approved by default. Check for
// such logic here, and set the approved flag on the authorization request accordingly.
authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
(Authentication) principal);
// TODO: is this call necessary?
boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
authorizationRequest.setApproved(approved);
// Validation is all done, so we can check for auto approval...
if (authorizationRequest.isApproved()) {
if (responseTypes.contains("token")) {
return getImplicitGrantResponse(authorizationRequest);
}
if (responseTypes.contains("code")) {
return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
(Authentication) principal));
}
}
// Place auth request into the model so that it is stored in the session
// for approveOrDeny to use. That way we make sure that auth request comes from the session,
// so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
model.put("authorizationRequest", authorizationRequest);
return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
}
catch (RuntimeException e) {
sessionStatus.setComplete();
throw e;
}
}
private OAuth2AccessToken getAccessTokenForImplicitGrant(TokenRequest tokenRequest,
OAuth2Request storedOAuth2Request) {
OAuth2AccessToken accessToken = null;
// These 1 method calls have to be atomic, otherwise the ImplicitGrantService can have a race condition where
// one thread removes the token request before another has a chance to redeem it.
synchronized (this.implicitLock) {
accessToken = getTokenGranter().grant("implicit",
new ImplicitTokenRequest(tokenRequest, storedOAuth2Request));
}
return accessToken;
}
.
.
.
I have also instructed the framework to change the mappring from /oauth/authorize to /custom/oauth/authorize:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore())
.accessTokenConverter(tokenEnhancer()).pathMapping("/oauth/authorize", "/custom/authorize/");
}
but when I run the code I encounter the following error:
Description:
Field tokenGranter in com.example.demo.controller.AuthorizationEndpointCustom required a bean of type 'org.springframework.security.oauth2.provider.TokenGranter' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.security.oauth2.provider.TokenGranter' in your configuration.
the parent class of AuthorizationEndpoint (AbstractEndpoint) declares tokenGranter but it is not instantiated. there is no #autowired for this and other attributes of this class. who does genereate and inject these variable into this class?
how can I get hold of tokenGranter obj and inject it?
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.security.oauth2.provider.endpoint;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.util.Assert;
/**
* #author Dave Syer
*
*/
public class AbstractEndpoint implements InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private WebResponseExceptionTranslator providerExceptionHandler = new DefaultWebResponseExceptionTranslator();
private TokenGranter tokenGranter;
private ClientDetailsService clientDetailsService;
private OAuth2RequestFactory oAuth2RequestFactory;
private OAuth2RequestFactory defaultOAuth2RequestFactory;
public void afterPropertiesSet() throws Exception {
Assert.state(tokenGranter != null, "TokenGranter must be provided");
Assert.state(clientDetailsService != null, "ClientDetailsService must be provided");
defaultOAuth2RequestFactory = new DefaultOAuth2RequestFactory(getClientDetailsService());
if (oAuth2RequestFactory == null) {
oAuth2RequestFactory = defaultOAuth2RequestFactory;
}
}
public void setProviderExceptionHandler(WebResponseExceptionTranslator providerExceptionHandler) {
this.providerExceptionHandler = providerExceptionHandler;
}
public void setTokenGranter(TokenGranter tokenGranter) {
this.tokenGranter = tokenGranter;
}
protected TokenGranter getTokenGranter() {
return tokenGranter;
}
protected WebResponseExceptionTranslator getExceptionTranslator() {
return providerExceptionHandler;
}
protected OAuth2RequestFactory getOAuth2RequestFactory() {
return oAuth2RequestFactory;
}
protected OAuth2RequestFactory getDefaultOAuth2RequestFactory() {
return defaultOAuth2RequestFactory;
}
public void setOAuth2RequestFactory(OAuth2RequestFactory oAuth2RequestFactory) {
this.oAuth2RequestFactory = oAuth2RequestFactory;
}
protected ClientDetailsService getClientDetailsService() {
return clientDetailsService;
}
public void setClientDetailsService(ClientDetailsService clientDetailsService) {
this.clientDetailsService = clientDetailsService;
}
}
I am answering my own question.
I took a good look at the framework code and I found out that AuthorizationServerEndpointsConfiguration class creates an object of type AuthorizationEndpoint and populates it's attributes and then return this object as a bean.
I managed to solve above mentioned problem with TokenGranter by creating a bean of my new AuthorizationEndpointCustom the same way AuthorizationServerEndpointsConfiguration does. this is the code to do so:
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
AuthorizationServerEndpointsConfiguration asec;
#Bean
#Order(value = Ordered.HIGHEST_PRECEDENCE)
#Primary
public AuthorizationEndpoint authorizationEndpoint () throws Exception{
AuthorizationEndpointCustom authorizationEndpoint = new AuthorizationEndpointCustom();
FrameworkEndpointHandlerMapping mapping = asec.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(asec.getEndpointsConfigurer().getExceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(asec.getEndpointsConfigurer().getTokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(asec.getEndpointsConfigurer().getAuthorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(asec.getEndpointsConfigurer().getOAuth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(asec.getEndpointsConfigurer().getOAuth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(asec.getEndpointsConfigurer().getUserApprovalHandler());
return authorizationEndpoint;
}
private String extractPath(FrameworkEndpointHandlerMapping mapping, String page) {
String path = mapping.getPath(page);
if (path.contains(":")) {
return path;
}
return "forward:" + path;
}
but this did not result in what I hoped to. the new bean does not replace the bean from framework code. this situation with overriding beans led to another question:
how replace framework beans
buttom line, this is not the way to override the framework endpoints. you can simply create a controller with mappings for these endpoints (e.g /oauth/authorize or /oauth/token). automatically these mappings will get precedence over framework endpoints. for more info refer to spring doc

Spring boot AzureAD AADAuthenticationFilter checks for invalid issuer how do I overwrite or config this to different issuer

Using azure ad spring starter 2.1.6. with the AADAuthenticationFilter https://github.com/Microsoft/azure-spring-boot, the code goes to buildUserPrincipal within this there is a verify issuer call. The default appears below. My issuer is https://login.microsoftonline.com/uuid/v2.0 not the default thus it throws an error, how can I overwrite or config this to check against my issuer?
private ConfigurableJWTProcessor<SecurityContext> getAadJwtTokenValidator(JWSAlgorithm jwsAlgorithm) {
final ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
final JWSKeySelector<SecurityContext> keySelector =
new JWSVerificationKeySelector<>(jwsAlgorithm, keySource);
jwtProcessor.setJWSKeySelector(keySelector);
jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<SecurityContext>() {
#Override
public void verify(JWTClaimsSet claimsSet, SecurityContext ctx) throws BadJWTException {
super.verify(claimsSet, ctx);
final String issuer = claimsSet.getIssuer();
if (issuer == null || !issuer.contains("https://sts.windows.net/")
&& !issuer.contains("https://sts.chinacloudapi.cn/")) {
throw new BadJWTException("Invalid token issuer");
}
}
});
return jwtProcessor;
}

Agent not returning correct OID value when using SNMP4j (org.snmp4j) v3 and user authentication?

I have written an SNMP agent and registered a managed object (created/set a value of an MIB OID).
When I retrieve this value using SNMPv2c, the value is returned correctly - the PDU from ResponseEvent.getResponse has type=GET and the variable bindings have expected data - correct OID etc.
When I retrieve this value using SNMPv3 and user authentication, the value is not returned correctly - the PDU from ResponseEvent.getResponse has type=REPORT and the variable bindings have
a different OID from that in the request - from what I've read so far this indicates a config/authentication error.
Below is sample code (snippets) used for client & agent - please can you inform me how to create agent & client - where I'm going wrong?
// TestSNMPAgent:
public class TestSNMPAgent {
private OID sysDescr = new OID("1.3.6.1.2.1.1.1.0");
...
public static void main(String[] args) throws IOException {
TestSNMPAgent agent = new TestSNMPAgent();
agent.init("0.0.0.0/4071");
private void init(String agentIp) throws IOException {
agent = new SNMPAgent(agentIp);
agent.start();
agent.unregisterManagedObject(agent.getSnmpv2MIB());
agent.registerManagedObject(new MOScalar(oid,
MOAccessImpl.ACCESS_READ_WRITE,
getVariable(value),sysDescr,
"1")));
...
}
}
// SNMPAgent:
public class SNMPAgent extends BaseAgent {
...
#Override
protected void addUsmUser(USM arg0) {
UsmUser user = new UsmUser(new OctetString("SHADES"),
AuthSHA.ID,
new OctetString("SHADESAuthPassword"),
PrivDES.ID,
new OctetString("SHADESPrivPassword"));
}
#Override
protected void addViews(VacmMIB vacm) {
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHADES"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v3group"), new OctetString(),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.NOAUTH_NOPRIV, VacmMIB.vacmExactMatch,
new OctetString("fullReadView"),
new OctetString("fullWriteView"),
new OctetString("fullNotifyView"),
StorageType.nonVolatile);
}
public void registerManagedObject(ManagedObject mo) {
try {
server.register(mo, null);
} catch (DuplicateRegistrationException ex) {
throw new RuntimeException(ex);
}
}
// TestSNMPMgr
public class TestSNMPMgr {
public static void main(String[] args) throws IOException {
TestSNMPMgr client = new TestSNMPMgr();
client.init();
}
public void init() {
SNMPMgr client = new SNMPMgr();
client.start();
// Get back Value which is set
String value = client.getAsString(new OID("1.3.6.1.2.1.1.1.0"));
}
}
// SNMPMgr
public class SNMPMgr {
Snmp snmp = null;
Address address = null;
public SNMPMgr()
{
address = "1.3.6.1.2.1.1.1.0";
}
/**
* Start the Snmp session. If you forget the listen() method you will not
* get any answers because the communication is asynchronous
* and the listen() method listens for answers.
* #throws IOException
*/
public void start() throws IOException {
address = GenericAddress.parse("udp:127.0.0.1/4701");
TransportMapping transport = new DefaultUdpTransportMapping();
snmp = new Snmp(transport);
USM usm = new USM(SecurityProtocols.getInstance(),
new OctetString(MPv3.createLocalEngineID()), 0);
SecurityModels.getInstance().addSecurityModel(usm);
transport.listen();
}
public void end() {
try {
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Method which takes a single OID and returns the response from the agent as a String.
* #param oid
* #return
* #throws IOException
*/
public String getAsString(OID oid) throws IOException {
ResponseEvent event = get(new OID[] { oid });
return event.getResponse().get(0).getVariable().toString();
}
public ResponseEvent get(OID oids[]) throws IOException {
PDU pdu = new ScopedPDU();
for (OID oid : oids) {
pdu.add(new VariableBinding(oid));
}
pdu.setType(PDU.GET);
// add user to the USM
snmp.getUSM().addUser(new OctetString("SHADES"),
new UsmUser(new OctetString("SHADES"),
AuthSHA.ID,
new OctetString("SHADESAuthPassword"),
PrivDES.ID,
new OctetString("SHADESPrivPassword")));
// send the PDU
ResponseEvent event = snmp.send(pdu, getTarget(), null);
if(event != null) {
return event;
}
throw new RuntimeException("GET timed out");
}
/**
* This method returns a Target, which contains information about
* where the data should be fetched and how.
* #return
*/
private UserTarget getTarget() {
UserTarget target = new UserTarget();
target.setAddress(address);
target.setRetries(1);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version3);
target.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
target.setSecurityName(new OctetString("SHADES"));
return target;
}
}
The OID in the Report PDU should tell you what is happening. Under typical circumstances there will be one or two (or one of two) request/report exchanges to establish initial SNMPv3 communications between manager and agent (or, rather, non-authoritative and authoritative engines, respectively).
The first is typically a usmStatUnknownEngineIDs report that allows the manager to discover the agent's Engine ID (needed for key localization/etc.) and will happen if you don't specify the proper Engine ID in the initial request. The second/other happens if using auth/noPriv or auth/priv level security, and that is usmStatsNotInTimeWindows, which is sent if the request doesn't specify Engine Boots/Engine Time values within proper range of the agent's values. These values prevent message replay attacks by making requests no longer valid if they fall out of the time window, and the manager typically doesn't know what they are until it receives them from the agent by way of a Report PDU.
After the manager has the proper Engine ID, Boots, and Time, and has localized keys to the Engine ID if necessary, then the normal request/response exchange can proceed as expected. Some SNMP APIs will take care of this exchange for you so you just send your request and get the eventual result after the exchange. It would seem that SNMP4j doesn't and you may have to handle it yourself if it's one of these reports.
If it's not one of these reports, then you likely have a mismatch in configuration.

#MessageMapping with placeholders

I am working with Spring-websocket and I have the following problem:
I am trying to put a placeholder inside a #MessageMapping annotation in order to get the url from properties. It works with #RequestMapping but not with #MessageMapping.
If I use this placeholder, the URL is null. Any idea or suggestion?
Example:
#RequestMapping(value= "${myProperty}")
#MessageMapping("${myProperty}")
Rossen Stoyanchev added placeholder support for #MessageMapping and #SubscribeMapping methods.
See Jira issue: https://jira.spring.io/browse/SPR-13271
Spring allows you to use property placeholders in #RequestMapping, but not in #MessageMapping. This is 'cause the MessageHandler. So, we need to override the default MessageHandler to do this.
WebSocketAnnotationMethodMessageHandler does not support placeholders and you need add this support yourself.
For simplicity I just created another WebSocketAnnotationMethodMessageHandler class in my project at the same package of the original, org.springframework.web.socket.messaging, and override getMappingForMethod method from SimpAnnotationMethodMessageHandler with same content, changing only how SimpMessageMappingInfo is contructed using this with this methods (private in WebSocketAnnotationMethodMessageHandler):
private SimpMessageMappingInfo createMessageMappingCondition(final MessageMapping annotation) {
return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
private SimpMessageMappingInfo createSubscribeCondition(final SubscribeMapping annotation) {
final SimpMessageTypeMessageCondition messageTypeMessageCondition = SimpMessageTypeMessageCondition.SUBSCRIBE;
return new SimpMessageMappingInfo(messageTypeMessageCondition, new DestinationPatternsMessageCondition(
this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}
These methods now will resolve value considering properties (calling resolveAnnotationValues method), so we need use something like this:
private String[] resolveAnnotationValues(final String[] destinationNames) {
final int length = destinationNames.length;
final String[] result = new String[length];
for (int i = 0; i < length; i++) {
result[i] = this.resolveAnnotationValue(destinationNames[i]);
}
return result;
}
private String resolveAnnotationValue(final String name) {
if (!(this.getApplicationContext() instanceof ConfigurableApplicationContext)) {
return name;
}
final ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) this.getApplicationContext();
final ConfigurableBeanFactory configurableBeanFactory = applicationContext.getBeanFactory();
final String placeholdersResolved = configurableBeanFactory.resolveEmbeddedValue(name);
final BeanExpressionResolver exprResolver = configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null) {
return name;
}
final Object result = exprResolver.evaluate(placeholdersResolved, new BeanExpressionContext(configurableBeanFactory, null));
return result != null ? result.toString() : name;
}
You still need to define a PropertySourcesPlaceholderConfigurer bean in your configuration.
If you are using XML based configuration, include something like this:
<context:property-placeholder location="classpath:/META-INF/spring/url-mapping-config.properties" />
If you are using Java based configuration, you can try in this way:
#Configuration
#PropertySources(value = #PropertySource("classpath:/META-INF/spring/url-mapping-config.properties"))
public class URLMappingConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Obs.: in this case, url-mapping-config.properties file are in a gradle/maven project in src\main\resources\META-INF\spring folder and content look like this:
myPropertyWS=urlvaluews
This is my sample controller:
#Controller
public class WebSocketController {
#SendTo("/topic/test")
#MessageMapping("${myPropertyWS}")
public String test() throws Exception {
Thread.sleep(4000); // simulated delay
return "OK";
}
}
With default MessageHandler startup log will print something like this:
INFO: Mapped "{[/${myPropertyWS}],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
And with our MessageHandler now print this:
INFO: Mapped "{[/urlvaluews],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception
See in this gist the full WebSocketAnnotationMethodMessageHandler implementation.
EDIT: this solution resolves the problem for versions before 4.2 GA. For more information, see this jira.
Update :
Now I understood what you mean, but I think that is not possible(yet).
Documentation does not mention anything related to Path mapping URIs.
Old answer
Use
#MessageMapping("/handler/{myProperty}")
instead of
#MessageMapping("/handler/${myProperty}")
And use it like this:
#MessageMapping("/myHandler/{username}")
public void handleTextMessage(#DestinationVariable String username,Message message) {
//do something
}
#MessageMapping("/chat/{roomId}")
public Message handleMessages(#DestinationVariable("roomId") String roomId, #Payload Message message, Traveler traveler) throws Exception {
System.out.println("Message received for room: " + roomId);
System.out.println("User: " + traveler.toString());
// store message in database
message.setAuthor(traveler);
message.setChatRoomId(Integer.parseInt(roomId));
int id = MessageRepository.getInstance().save(message);
message.setId(id);
return message;
}

Resources