Below is the code I tried.
#Bean
public MultiResourceItemReader<Map<String, String>> multiResourceItemReader() throws FileNotFoundException {
MultiResourceItemReader<Map<String, String>> resourceItemReader = new MultiResourceItemReader<Map<String, String>>();
inputResources=getMultipleResourceItemreader();
resourceItemReader.setResources(inputResources);
resourceItemReader.setDelegate(reader());
return resourceItemReader;
}
You can use ResourceAware interface to get the resource name.Your Iteam should implement ResourceAware interface.
class Foo implements ResourceAware {
String value;
Resource resource;
Foo(String value) {
this.value = value;
}
#Override
public void setResource(Resource resource) {
this.resource = resource;
}
}
}
Related
We are trying to implement SMART On FHIR healthcare authorization protocol specification. This spec is an extension to OIDC (open id connect protocol). In SMART on FHIR, we need to add extra claims called 'patient' with value say '123' in AccessTokenResponse object during the OAUTH dance.
In order to accomplish this, I tried to extended the OIDCLoginProtocol and OIDCLoginProtocolFactory classes and given a new name to this protocol called 'smart-openid-connect'. I created this as a SPI (service provider interface) JAR and copied it to /standalone/deployments folder. Now, I can see the new protocol called 'smart-openid-connect' in the UI, but it does not show Access Type options in the client creation screen to select as a confidential client. Hence, I am not able to create client secrets as the Credentials menu is not appearing for this new protocol.
I have the following questions:
How to enable the Credentials tab in the client creation screen using SPI for the new protocol that I created.?
Which class I need to override to add extra claims in AccessTokenResponse ?
Kindly help me in this regard.
Thanks for your help in advance.
I have followed your steps for developing our custom protocol. When we migrate our company existed authentication protocol, I have used org.keycloak.adapters.authentication.ClientCredentialsProvider, org.keycloak.authentication.ClientAuthenticatorFactory, org.keycloak.authentication.ClientAuthenticator classes for defining our custom protocol. Credentials tab is only visible if oidc and confidential choices are selected. It is defined on UI javascript codes. So we choose oidc option for setting custom protocol. Afterwards, we return back to our custom protocol.
XyzClientAuthenticatorFactory
public class XyzClientAuthenticatorFactory implements ClientAuthenticatorFactory, ClientAuthenticator {
public static final String PROVIDER_ID = "xyz-client-authenticator";
public static final String DISPLAY_TEXT = "Xyz Client Authenticator";
public static final String REFERENCE_CATEGORY = null;
public static final String HELP_TEXT = null;
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
private AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED,
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
AuthenticationExecutionModel.Requirement.DISABLED};
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(Constants.CLIENT_SETTINGS_APP_ID);
property.setLabel("Xyz App Id");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(Constants.CLIENT_SETTINGS_APP_KEY);
property.setLabel("Xyz App Key");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
#Override
public void authenticateClient(ClientAuthenticationFlowContext context) {
}
#Override
public String getDisplayType() {
return DISPLAY_TEXT;
}
#Override
public String getReferenceCategory() {
return REFERENCE_CATEGORY;
}
#Override
public ClientAuthenticator create() {
return this;
}
#Override
public boolean isConfigurable() {
return false;
}
#Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}
#Override
public boolean isUserSetupAllowed() {
return false;
}
#Override
public List<ProviderConfigProperty> getConfigPropertiesPerClient() {
return configProperties;
}
#Override
public Map<String, Object> getAdapterConfiguration(ClientModel client) {
Map<String, Object> result = new HashMap<>();
result.put(Constants.CLIENT_SETTINGS_APP_ID, client.getAttribute(Constants.CLIENT_SETTINGS_APP_ID));
result.put(Constants.CLIENT_SETTINGS_APP_KEY, client.getAttribute(Constants.CLIENT_SETTINGS_APP_KEY));
return result;
}
#Override
public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
Set<String> results = new LinkedHashSet<>();
results.add(Constants.CLIENT_SETTINGS_APP_ID);
results.add(Constants.CLIENT_SETTINGS_APP_KEY);
return results;
} else {
return Collections.emptySet();
}
}
#Override
public String getHelpText() {
return HELP_TEXT;
}
#Override
public List<ProviderConfigProperty> getConfigProperties() {
return new LinkedList<>();
}
#Override
public ClientAuthenticator create(KeycloakSession session) {
return this;
}
#Override
public void init(Config.Scope config) {
}
#Override
public void postInit(KeycloakSessionFactory factory) {
}
#Override
public void close() {
}
#Override
public String getId() {
return PROVIDER_ID;
}
}
XyzClientCredential
public class XyzClientCredential implements ClientCredentialsProvider {
public static final String PROVIDER_ID = "xyz-client-credential";
#Override
public String getId() {
return PROVIDER_ID;
}
#Override
public void init(KeycloakDeployment deployment, Object config) {
}
#Override
public void setClientCredentials(KeycloakDeployment deployment, Map<String, String> requestHeaders, Map<String, String> formParams) {
}
}
XyzLoginProtocolFactory
public class XyzLoginProtocolFactory implements LoginProtocolFactory {
static {
}
#Override
public Map<String, ProtocolMapperModel> getBuiltinMappers() {
return new HashMap<>();
}
#Override
public Object createProtocolEndpoint(RealmModel realm, EventBuilder event) {
return new XyzLoginProtocolService(realm, event);
}
protected void addDefaultClientScopes(RealmModel realm, ClientModel newClient) {
addDefaultClientScopes(realm, Arrays.asList(newClient));
}
protected void addDefaultClientScopes(RealmModel realm, List<ClientModel> newClients) {
Set<ClientScopeModel> defaultClientScopes = realm.getDefaultClientScopes(true).stream()
.filter(clientScope -> getId().equals(clientScope.getProtocol()))
.collect(Collectors.toSet());
for (ClientModel newClient : newClients) {
for (ClientScopeModel defaultClientScopeModel : defaultClientScopes) {
newClient.addClientScope(defaultClientScopeModel, true);
}
}
Set<ClientScopeModel> nonDefaultClientScopes = realm.getDefaultClientScopes(false).stream()
.filter(clientScope -> getId().equals(clientScope.getProtocol()))
.collect(Collectors.toSet());
for (ClientModel newClient : newClients) {
for (ClientScopeModel nonDefaultClientScope : nonDefaultClientScopes) {
newClient.addClientScope(nonDefaultClientScope, true);
}
}
}
#Override
public void createDefaultClientScopes(RealmModel newRealm, boolean addScopesToExistingClients) {
// Create default client scopes for realm built-in clients too
if (addScopesToExistingClients) {
addDefaultClientScopes(newRealm, newRealm.getClients());
}
}
#Override
public void setupClientDefaults(ClientRepresentation rep, ClientModel newClient) {
}
#Override
public LoginProtocol create(KeycloakSession session) {
return new XyzLoginProtocol().setSession(session);
}
#Override
public void init(Config.Scope config) {
log.infof("XyzLoginProtocolFactory init");
}
#Override
public void postInit(KeycloakSessionFactory factory) {
factory.register(event -> {
if (event instanceof RealmModel.ClientCreationEvent) {
ClientModel client = ((RealmModel.ClientCreationEvent)event).getCreatedClient();
addDefaultClientScopes(client.getRealm(), client);
addDefaults(client);
}
});
}
protected void addDefaults(ClientModel client) {
}
#Override
public void close() {
}
#Override
public String getId() {
return XyzLoginProtocol.LOGIN_PROTOCOL;
}
}
XyzLoginProtocol
public class XyzLoginProtocol implements LoginProtocol {
public static final String LOGIN_PROTOCOL = "xyz";
protected KeycloakSession session;
protected RealmModel realm;
protected UriInfo uriInfo;
protected HttpHeaders headers;
protected EventBuilder event;
public XyzLoginProtocol(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, EventBuilder event) {
this.session = session;
this.realm = realm;
this.uriInfo = uriInfo;
this.headers = headers;
this.event = event;
}
public XyzLoginProtocol() {
}
#Override
public XyzLoginProtocol setSession(KeycloakSession session) {
this.session = session;
return this;
}
#Override
public XyzLoginProtocol setRealm(RealmModel realm) {
this.realm = realm;
return this;
}
#Override
public XyzLoginProtocol setUriInfo(UriInfo uriInfo) {
this.uriInfo = uriInfo;
return this;
}
#Override
public XyzLoginProtocol setHttpHeaders(HttpHeaders headers) {
this.headers = headers;
return this;
}
#Override
public XyzLoginProtocol setEventBuilder(EventBuilder event) {
this.event = event;
return this;
}
#Override
public Response authenticated(AuthenticationSessionModel authSession, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
log.debugf("Authenticated.. User: %s, Session Id: %s", userSession.getUser().getUsername(), userSession.getId());
try {
....
} catch (Exception ex) {
// TODO handle TokenNotFoundException exception
log.error(ex.getMessage(), ex);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
#Override
public Response sendError(AuthenticationSessionModel authSession, Error error) {
new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authSession, true);
String redirect = authSession.getRedirectUri();
try {
URI uri = new URI(redirect);
return Response.status(302).location(uri).build();
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
return Response.noContent().build();
}
}
#Override
public void backchannelLogout(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
ClientModel client = clientSession.getClient();
new ResourceAdminManager(session).logoutClientSession(realm, client, clientSession);
}
#Override
public Response frontchannelLogout(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
throw new RuntimeException("NOT IMPLEMENTED");
}
#Override
public Response finishLogout(UserSessionModel userSession) {
return Response.noContent().build();
}
#Override
public boolean requireReauthentication(UserSessionModel userSession, AuthenticationSessionModel authSession) {
return false;
}
#Override
public boolean sendPushRevocationPolicyRequest(RealmModel realm, ClientModel resource, int notBefore, String managementUrl) {
PushNotBeforeAction adminAction = new PushNotBeforeAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getClientId(), notBefore);
String token = session.tokens().encode(adminAction);
log.tracev("pushRevocation resource: {0} url: {1}", resource.getClientId(), managementUrl);
URI target = UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_PUSH_NOT_BEFORE).build();
try {
int status = session.getProvider(HttpClientProvider.class).postText(target.toString(), token);
boolean success = status == 204 || status == 200;
log.tracef("pushRevocation success for %s: %s", managementUrl, success);
return success;
} catch (IOException e) {
ServicesLogger.LOGGER.failedToSendRevocation(e);
return false;
}
}
#Override
public void close() {
}
}
XyzLoginProtocolService
public class XyzLoginProtocolService {
private final RealmModel realm;
private final EventBuilder event;
#Context
private KeycloakSession session;
#Context
private HttpHeaders headers;
#Context
private HttpRequest request;
#Context
private ClientConnection clientConnection;
public XyzLoginProtocolService(RealmModel realm, EventBuilder event) {
this.realm = realm;
this.event = event;
this.event.realm(realm);
}
#POST
#Path("request")
#Produces(MediaType.APPLICATION_JSON)
#NoCache
public Response request(ApipmLoginRequest loginRequest) {
....
}
The documentation for ReplyingKafkaTemplate which provides Request-Reply support (introduced in Spring-Kafka 2.1.3) suggests that different types may be used for the Request and Reply:
ReplyingKafkaTemplate<K, V, R>
where the parameterised type K designates the message Key, V designates the Value (i.e the Request), and R designates the Reply.
So good so far. But the corresponding supporting classes for implementing the server side Request-Reply doesn't seem to support different types for V, R. The documentation suggests using a KafkaListener with an added #SendTo annotation, which behind the scene uses a configured replyTemplate on the MessageListenerContainer. But the AbstractKafkaListenerEndpoint only supports a single type for the listener as well as the replyTemplate:
public abstract class AbstractKafkaListenerEndpoint<K, V>
implements KafkaListenerEndpoint, BeanFactoryAware, InitializingBean {
...
/**
* Set the {#link KafkaTemplate} to use to send replies.
* #param replyTemplate the template.
* #since 2.0
*/
public void setReplyTemplate(KafkaTemplate<K, V> replyTemplate) {
this.replyTemplate = replyTemplate;
}
...
}
hence V and R needs to be the same type.
The example used in the documentation indeed uses String for both Request and Reply.
Am I missing something, or is this a design flaw in the Spring-Kafka Request-Reply support that should be reported and corrected?
This is fixed in the 2.2 release.
For earlier versions, simply inject a raw KafkaTemplate (with no generics).
EDIT
#SpringBootApplication
public class So53151961Application {
public static void main(String[] args) {
SpringApplication.run(So53151961Application.class, args);
}
#KafkaListener(id = "so53151961", topics = "so53151961")
#SendTo
public Bar handle(Foo foo) {
System.out.println(foo);
return new Bar(foo.getValue().toUpperCase());
}
#Bean
public ReplyingKafkaTemplate<String, Foo, Bar> replyingTemplate(ProducerFactory<String, Foo> pf,
ConcurrentKafkaListenerContainerFactory<String, Bar> factory) {
ConcurrentMessageListenerContainer<String, Bar> replyContainer =
factory.createContainer("so53151961-replyTopic");
replyContainer.getContainerProperties().setGroupId("so53151961.reply");
ReplyingKafkaTemplate<String, Foo, Bar> replyingKafkaTemplate = new ReplyingKafkaTemplate<>(pf, replyContainer);
return replyingKafkaTemplate;
}
#Bean
public KafkaTemplate<String, Bar> replyTemplate(ProducerFactory<String, Bar> pf,
ConcurrentKafkaListenerContainerFactory<String, Bar> factory) {
KafkaTemplate<String, Bar> kafkaTemplate = new KafkaTemplate<>(pf);
factory.setReplyTemplate(kafkaTemplate);
return kafkaTemplate;
}
#Bean
public ApplicationRunner runner(ReplyingKafkaTemplate<String, Foo, Bar> template) {
return args -> {
ProducerRecord<String, Foo> record = new ProducerRecord<>("so53151961", null, "key", new Foo("foo"));
RequestReplyFuture<String, Foo, Bar> future = template.sendAndReceive(record);
System.out.println(future.get(10, TimeUnit.SECONDS).value());
};
}
#Bean
public NewTopic topic() {
return new NewTopic("so53151961", 1, (short) 1);
}
#Bean
public NewTopic reply() {
return new NewTopic("so53151961-replyTopic", 1, (short) 1);
}
public static class Foo {
public String value;
public Foo() {
super();
}
public Foo(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return "Foo [value=" + this.value + "]";
}
}
public static class Bar {
public String value;
public Bar() {
super();
}
public Bar(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return "Bar [value=" + this.value + "]";
}
}
}
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.enable-auto-commit=false
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.properties.spring.json.trusted.packages=com.example
result
Foo [value=foo]
Bar [value=FOO]
I'm new to spring mvc and I wish to create a RESTful service.
I create a controller,maps a function to URL and when I try to access it instead of a JSON I receive a 406 error.
I guess there is some trouble with the configuration(Java configuration) but I can't find what it is.
here is my WebConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.yated.web.controller")
public class WebConfig {
}
here is my WebInitializer:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
and here is the controller:
#RestController
public class EventsController {
#RequestMapping(value = "/events")
public List<String> getEvents()
{
List<String> list = new ArrayList<String>();
list.add("Event1");
list.add("Event2");
return list;
}
}
and as I said when I try to access:
http://localhost:8181/web/events
or
http://localhost:8181/web/events.json
I get http error 406
Any ideas?
I need to pass a map with a custom field to client.
just TreeMap works fine, but not descendants of TreeMap. If I change TreeMap to HashMap, it also works fine.
GWT 2.5.1
public class MyMap extends TreeMap<String, String> {
public String s;
public MyMap() {
}
public MyMap(String s) {
this.s = s;
}
}
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {
#Override
public Map<String, String> testSelfMap() {
Map<String, String> c = new MyMap("abc");
c.put("k", "v");
return c;
}
}
#RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
Map<String, String> testSelfMap();
}
public interface GreetingServiceAsync {
void testSelfMap(AsyncCallback<Map<String, String>> async);
}
public class HW implements EntryPoint {
GreetingServiceAsync serv = GWT.create(GreetingService.class);
public void onModuleLoad() {
serv.testSelfMap(new AsyncCallback<Map<String, String>>() {
#Override
public void onSuccess(Map<String, String> result) {
System.out.println(result.get("k"));
}
#Override
public void onFailure(Throwable caught) {
caught.printStackTrace();
}
});
}
}
I am using jersey as rest client, but I have problems when application is actually deployed as war on Glassfish 4. When I run it from test it works okay, but in deployed application mapping to POJO return all properties as null.
I tried MoxyJsonProvider, JacksonJsonProvider also custom GsonProvider, but result is same.
Client constructor:
public DockerClientImpl(String restApiUrl) {
restURL = restApiUrl;
restClient = ClientBuilder
.newBuilder()
.register(GsonProvider.class)
.build();
}
One of get methods:
#Override
public List<Container> getContainers(boolean all, boolean latest, int limit, boolean showSize, String since, String before) throws DockerException {
Response response = restClient
.target(restURL)
.path(CONTAINERS_LIST)
.queryParam("all", all)
.queryParam("limit", limit)
.queryParam("since", since)
.queryParam("before", before)
.queryParam("size", showSize)
.request(MediaType.APPLICATION_JSON)
.get();
switch (response.getStatus()) {
case 200:
LOGGER.info("Container list succesfully retrieved");
break;
case 400:
LOGGER.error("Bad request parameter");
throw new DockerException("Bad request parameter");
case 500:
LOGGER.error("Docker Server Error");
throw new DockerException("Docker Server Error");
default:
throw new DockerException("Unknown Error");
}
Type type = new TypeToken<Collection<Container>>() {
}.getType();
List<Container> result = response.readEntity(new GenericType<List<Container>>() { });
LOGGER.debug(String.format("Response: %s", result));
return result;
}
Container POJO:
#SerializedName("Id")
private String id;
#SerializedName("Command")
private String command;
#SerializedName("Image")
private String image;
#SerializedName("Created")
private long created;
#SerializedName("Status")
private String status;
#SerializedName("Ports")
private Port[] ports; //Example value "49164->6900, 49165->7100"
#SerializedName("SizeRw")
private int size;
#SerializedName("SizeRootFs")
private int sizeRootFs;
#SerializedName("Names")
private String[] names;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public long getCreated() {
return created;
}
public void setCreated(long created) {
this.created = created;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Port[] getPorts() {
return ports;
}
public void setPorts(Port[] ports) {
this.ports = ports;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getSizeRootFs() {
return sizeRootFs;
}
public void setSizeRootFs(int sizeRootFs) {
this.sizeRootFs = sizeRootFs;
}
public String[] getNames() {
return names;
}
public void setNames(String[] names) {
this.names = names;
}
And currently used Gson Provider:
#Provider
#Consumes({MediaType.APPLICATION_JSON, "text/json"})
#Produces({MediaType.APPLICATION_JSON, "text/json"})
public class GsonProvider implements MessageBodyWriter<Object>,
MessageBodyReader<Object> {
private static final String UTF_8 = "UTF-8";
private Gson gson;
public GsonProvider() {
}
private Gson getGson() {
if (gson == null) {
final GsonBuilder gsonBuilder = new GsonBuilder();
gson = gsonBuilder.create();
}
return gson;
}
#Override
public boolean isReadable(Class<?> type, Type genericType,
java.lang.annotation.Annotation[] annotations, MediaType mediaType) {
return true;
}
#Override
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
try (InputStreamReader streamReader = new InputStreamReader(entityStream, UTF_8)) {
Type jsonType;
if (type.equals(genericType)) {
jsonType = type;
} else {
jsonType = genericType;
}
return getGson().fromJson(streamReader, jsonType);
}
}
#Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
#Override
public long getSize(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1;
}
#Override
public void writeTo(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
try (OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8)) {
Type jsonType;
if (type.equals(genericType)) {
jsonType = type;
} else {
jsonType = genericType;
}
getGson().toJson(object, jsonType, writer);
}
}
}
Output in tests:
Container{id=a9e3a67979d392a0e48d534db5184ce717b0629c4255d4dbc1373f5a9140df51, command=/bin/sh -c /usr/sbin/sshd -D, image=frantiseks/apac:latest, created=1388002251, status=Up About an hour, ports=[Lcz.utb.fai.apac.entity.Port;#69ac536b, size=0, sizeRootFs=0, names=[Ljava.lang.String;#3098cc00}
Output from deployed application:
[Container{id=null, command=null, image=null, created=0, status=null, ports=null, size=0, sizeRootFs=0, names=null}, Container{id=null, command=null, image=null, created=0, status=null, ports=null, size=0, sizeRootFs=0, names=null}]
So could someone tell me why it do not work in glassfish? Or what I am doing wrong?
Thanks.