Adding custom logic in Odata Create Entity call in java code - java-8

In the project am using olingo 2.0.12 jar in the java code.
During the create Entity service call ,
Is there a way to check for which entity data insert requested and,
Alter column values / append new column values before data persisted?
Is there a way to add above?
Code snippet given below,
public class A extends ODataJPADefaultProcessor{
#Override
public ODataResponse createEntity(final PostUriInfo uriParserResultView, final InputStream content,
final String requestContentType, final String contentType) throws ODataJPAModelException,
ODataJPARuntimeException, ODataNotFoundException, EdmException, EntityProviderException {
// Need to check the entity name and need to alter/add column values
}
}

Yes one of the possible ways would be to create your own CustomODataJPAProcessor which extends ODataJPADefaultProcessor.
You will have to register this in JPAServiceFactory by overriding the method
#Override
public ODataSingleProcessor createCustomODataProcessor(ODataJPAContext oDataJPAContext) {
return new CustomODataJPAProcessor(this.oDataJPAContext);
}
Now Olingo will use CustomODataJPAProcessor which can implement the following code to check the entities and transform them if needed
Sample code of CustomODataJPAProcessor
public class CustomODataJPAProcessor extends ODataJPADefaultProcessor {
Logger LOG = LoggerFactory.getLogger(this.getClass());
public CustomODataJPAProcessor(ODataJPAContext oDataJPAContext) {
super(oDataJPAContext);
}
#Override
public ODataResponse createEntity(final PostUriInfo uriParserResultView, final InputStream content,
final String requestContentType, final String contentType) throws ODataException {
ODataResponse oDataResponse = null;
oDataJPAContext.setODataContext(getContext());
InputStream forwardedInputStream = content;
try {
if (uriParserResultView.getTargetEntitySet().getName().equals("Students")) {
LOG.info("Students Entity Set Executed");
if (requestContentType.equalsIgnoreCase(ContentType.APPLICATION_JSON.toContentTypeString())) {
#SuppressWarnings("deprecation")
JsonElement elem = new JsonParser().parse(new InputStreamReader(content));
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
Student s = gson.fromJson(elem, Student.class);
// Change some values
s.setStudentID("Test" + s.getStudentID());
forwardedInputStream = new ByteArrayInputStream(gson.toJson(s).getBytes());
}
}
Object createdJpaEntity = jpaProcessor.process(uriParserResultView, forwardedInputStream,
requestContentType);
oDataResponse = responseBuilder.build(uriParserResultView, createdJpaEntity, contentType);
} catch (JsonIOException | JsonSyntaxException e) {
throw new RuntimeException(e);
} finally {
close();
}
return oDataResponse;
}
}
In Summery
Register your custom org.apache.olingo.odata2.service.factory Code Link
Create your own CustomODataJPAProcessor Code Link
Override createCustomODataProcessor in JPAServiceFactory to use the custom processor Code Link

Related

more than one 'primary' service instance suppliers found during load balancing (spring boot/cloud)

I'm currently updating from Spring boot 2.2.x to 2.6.x + legacy code, it's a big jump so there were multiple changes. I'm now running into a problem with load balancing through an api-gateway. I'll apologize in advance for the wall of code to come. I will put the point of failure at the bottom.
When I send in an API request, I get the following error:
more than one 'primary' bean found among candidates: [zookeeperDiscoveryClientServiceInstanceListSupplier, serviceInstanceListSupplier, retryAwareDiscoveryClientServiceInstanceListSupplier]
it seems that the zookeeperDiscovery and retryAware suppliers are loaded through the default serviceInsatnceListSupplier, which has #Primary over it. I thought would take precedence over the other ones. I assume I must be doing something wrong due changes in the newer version, here are the relevant code in question:
#Configuration
#LoadBalancerClients(defaultConfiguration = ClientConfiguration.class)
public class WebClientConfiguration {
#Bean
#Qualifier("microserviceWebClient")
#ConditionalOnMissingBean(name = "microserviceWebClient")
public WebClient microserviceWebClient(#Qualifier("microserviceWebClientBuilder") WebClient.Builder builder) {
return builder.build();
}
#Bean
#Qualifier("microserviceWebClientBuilder")
#ConditionalOnMissingBean(name = "microserviceWebClientBuilder")
#LoadBalanced
public WebClient.Builder microserviceWebClientBuilder() {
return WebClient.builder();
}
#Bean
#Primary
public ReactorLoadBalancerExchangeFilterFunction reactorLoadBalancerExchangeFilterFunction(
ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
//the transformer is currently null, there wasn't a transformer before the upgrade
return new CustomExchangeFilterFunction(loadBalancerFactory, transformer);
}
}
There are also some Feign Client related configs here which I will omit, since it's not (or shouldn't be) playing a role in this problem:
public class ClientConfiguration {
/**
* The property key within the feign clients configuration context for the feign client name.
*/
public static final String FEIGN_CLIENT_NAME_PROPERTY = "feign.client.name";
public ClientConfiguration() {
}
//Creates a new BiPredicate for shouldClose. This will be used to determine if HTTP Connections should be automatically closed or not.
#Bean
#ConditionalOnMissingBean
public BiPredicate<Response, Type> shouldClose() {
return (Response response, Type type) -> {
if(type instanceof Class) {
Class<?> currentClass = (Class<?>) type;
return (null == AnnotationUtils.getAnnotation(currentClass, EnableResponseStream.class));
}
return true;
};
}
//Creates a Custom Decoder
#Bean
public Decoder createCustomDecoder(
ObjectFactory<HttpMessageConverters> converters, BiPredicate<Response, Type> shouldClose
) {
return new CustomDecoder(converters, shouldClose);
}
#Bean
#Qualifier("loadBalancerName")
public String loadBalancerName(PropertyResolver propertyResolver) {
String name = propertyResolver.getProperty(FEIGN_CLIENT_NAME_PROPERTY);
if(StringUtils.hasText(name)) {
// we are in a feign context
return name;
}
// we are in a LoadBalancerClientFactory context
name = propertyResolver.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
Assert.notNull(name, "Could not find a load balancer name within the configuration context!");
return name;
}
#Bean
public ReactorServiceInstanceLoadBalancer reactorServiceInstanceLoadBalancer(
BeanFactory beanFactory, #Qualifier("loadBalancerName") String loadBalancerName
) {
return new CustomRoundRobinLoadBalancer(
beanFactory.getBeanProvider(ServiceInstanceListSupplier.class),
loadBalancerName
);
}
#Bean
#Primary
public ServiceInstanceListSupplier serviceInstanceListSupplier(
#Qualifier(
"filter"
) Predicate<ServiceInstance> filter, DiscoveryClient discoveryClient, Environment environment, #Qualifier(
"loadBalancerName"
) String loadBalancerName
) {
// add service name to environment if necessary
if(environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME) == null) {
StandardEnvironment wrapped = new StandardEnvironment();
if(environment instanceof ConfigurableEnvironment) {
((ConfigurableEnvironment) environment).getPropertySources()
.forEach(s -> wrapped.getPropertySources().addLast(s));
}
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put(LoadBalancerClientFactory.PROPERTY_NAME, loadBalancerName);
wrapped.getPropertySources().addLast(new MapPropertySource(loadBalancerName, additionalProperties));
environment = wrapped;
}
return new FilteringInstanceListSupplier(filter, discoveryClient, environment);
}
}
There was a change in the ExchangeFilter constructor, but as far as I can tell, it accepts that empty transformer,I don't know if it's supposed to:
public class CustomExchangeFilterFunction extends ReactorLoadBalancerExchangeFilterFunction {
private static final ThreadLocal<ClientRequest> REQUEST_HOLDER = new ThreadLocal<>();
//I think it's wrong but I don't know what to do here
private static List<LoadBalancerClientRequestTransformer> transformersList;
private final Factory<ServiceInstance> loadBalancerFactory;
public CustomExchangeFilterFunction (Factory<ServiceInstance> loadBalancerFactory) {
this(loadBalancerFactory);
///according to docs, but I don't know where and if I need to use this
#Bean
public LoadBalancerClientRequestTransformer transformer() {
return new LoadBalancerClientRequestTransformer() {
#Override
public ClientRequest transformRequest(ClientRequest request, ServiceInstance instance) {
return ClientRequest.from(request)
.header(instance.getInstanceId())
.build();
}
};
}
public CustomExchangeFilterFunction (Factory<ServiceInstance> loadBalancerFactory, List<LoadBalancerClientRequestTransformer> transformersList) {
super(loadBalancerFactory, transformersList); //the changed constructor
this.loadBalancerFactory = loadBalancerFactory;;
}
#Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
// put the current request into the thread context - ugly, but couldn't find a better way to access the request within
// the choose method without reimplementing nearly everything
REQUEST_HOLDER.set(request);
try {
return super.filter(request, next);
} finally {
REQUEST_HOLDER.remove();
}
}
//used to be an override, but the function has changed
//code execution doesn't even get this far yet
protected Mono<Response<ServiceInstance>> choose(String serviceId) {
ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerFactory.getInstance(serviceId);
if(loadBalancer == null) {
return Mono.just(new EmptyResponse());
}
ClientRequest request = REQUEST_HOLDER.get();
// this might be null, if the underlying implementation changed and this method is no longer executed in the same
// thread
// as the filter method
Assert.notNull(request, "request must not be null, underlying implementation seems to have changed");
return choose(loadBalancer, filter);
}
protected Mono<Response<ServiceInstance>> choose(
ReactiveLoadBalancer<ServiceInstance> loadBalancer,
Predicate<ServiceInstance> filter
) {
return Mono.from(loadBalancer.choose(new DefaultRequest<>(filter)));
}
}
There were pretty big changes in the CustomExchangeFilterFunction, but the current execution doesn't even get there. It fails here, in .getIfAvailable(...):
public class CustomRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final int DEFAULT_SEED_POSITION = 1000;
private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private final String serviceId;
private final int seedPosition;
private final AtomicInteger position;
private final Map<String, AtomicInteger> positionsForVersions = new HashMap<>();
public CustomRoundRobinLoadBalancer (
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId
) {
this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(DEFAULT_SEED_POSITION));
}
public CustomRoundRobinLoadBalancer (
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId,
int seedPosition
) {
Assert.notNull(serviceInstanceListSupplierProvider, "serviceInstanceListSupplierProvider must not be null");
Assert.notNull(serviceId, "serviceId must not be null");
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.serviceId = serviceId;
this.seedPosition = seedPosition;
this.position = new AtomicInteger(seedPosition);
}
#Override
// we have no choice but to use the raw type Request here, because this method overrides another one with this signature
public Mono<Response<ServiceInstance>> choose(#SuppressWarnings("rawtypes") Request request) {
//fails here!
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get().next().map((List<ServiceInstance> instances) -> getInstanceResponse(instances, request));
}
}
Edit: after some deeper stacktracing, it seems that it does go into the CustomFilterFunction and invokes the constructor with super(loadBalancerFactory, transformer)
I found the problem or a workaround. I was using #LoadBalancerClients because I thought it would just set the same config for all clients that way (even if I technically only have one atm). I changed it to ##LoadBalancerClient and it suddenly worked. I don't quite understand why this made a difference but it did!

Global Exception Handling in Spring Cloud Function on AWS Lambda Platform

I am using spring cloud function on AWS lambda. I am trying to achieve global exception handling like Spring Boot using #ExceptionHandler annotation. But this method is not getting executed and I am getting 500 for any type of exception.
Sample code is below-
#SpringBootApplication
public class App{
public static void main( String[] args ){
SpringApplication.run(App.class, args);
}
#Bean
public Function<Message<User>, User> getUser(){
return (message)->{
User u = message.getPayload();
if(u==null){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"No user details provided");
}
return u;
}
}
#ExceptionHandler(ResponseStatusException.class)
public APIGatewayProxyResponseEvent handleException(ResponseStatusException e){
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
response.setStatusCode(e.getRawStatusCode());
response.setBody(e.getMessage());
return response;
}
}
I am getting 500 in response instead Bad Request. Is there any way to achieve this scenario ?
You can provide your custom exceptionHandler while building SpringBootLambdaContainerHandler.
public class StreamLambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
handler = new SpringBootProxyHandlerBuilder<AwsProxyRequest>()
.defaultProxy()
.exceptionHandler(***your customer handler here***)
// other methods are skipped
.buildAndInitialize();
}
}
If you are using spring cloud functions no need to use SpringBootLambdaContainerHandler, what you need to do is create a custom routing function and handle the exception thrown from your lambda function and return APIGatewayProxyResponseEvent. below shows how I achieved the desired result
public class CustomRoutingFunction implements Function<Message<?>, APIGatewayProxyResponseEvent> {
private final FunctionCatalog functionCatalog;
private final FunctionProperties functionProperties;
private final MessageRoutingCallback routingCallback;
public static final String DEFAULT_ROUTE_HANDLER = "defaultMessageRoutingHandler";
public CustomRoutingFunction(FunctionCatalog functionCatalog,
FunctionProperties functionProperties,
MessageRoutingCallback routingCallback) {
this.functionCatalog = functionCatalog;
this.functionProperties = functionProperties;
this.routingCallback = routingCallback;
}
#Override
public APIGatewayProxyResponseEvent apply(Message<?> input) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
try {
String functionDefinition = this.routingCallback.routingResult(input);
SimpleFunctionRegistry.FunctionInvocationWrapper function = functionCatalog.lookup(functionDefinition);
Object output = function.apply(input);
String payload = mapper.writeValueAsString(output);
return new APIGatewayProxyResponseEvent()
.withIsBase64Encoded(false)
.withBody(payload)
.withHeaders(Map.of(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE,
"statusCode", "200"))
.withStatusCode(200);
} catch (Exception e) {
return new APIGatewayProxyResponseEvent()
.withIsBase64Encoded(false)
.withHeaders(Map.of(HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_JSON_VALUE,
"statusCode", String.valueOf(HttpStatus.BAD_REQUEST.value())))
.withBody(e.getMessage())
.withStatusCode(HttpStatus.BAD_REQUEST.value());
}
}
}
now you need to register your router function as a bean and pass it to spring.cloud.function.definition
#Bean
public CustomRoutingFunction customRoutingFunction(FunctionCatalog functionCatalog,
FunctionProperties functionProperties,
#Nullable MessageRoutingCallback routingCallback,
#Nullable DefaultMessageRoutingHandler defaultMessageRoutingHandler) {
if (defaultMessageRoutingHandler != null) {
FunctionRegistration functionRegistration = new FunctionRegistration(defaultMessageRoutingHandler, CustomRoutingFunction.DEFAULT_ROUTE_HANDLER);
functionRegistration.type(FunctionTypeUtils.consumerType(ResolvableType.forClassWithGenerics(Message.class, Object.class).getType()));
((FunctionRegistry) functionCatalog).register(functionRegistration);
}
return new CustomRoutingFunction(functionCatalog, functionProperties, routingCallback);
}
inside your application.yml file
spring:
cloud:
function:
definition: customRoutingFunction

Using a Wrapper Type for a DTO in Spring + Jackson

I'm trying to find a more or less elegant way to handle PATCH http operations in Spring MVC.
Basically, I'd like to perform a "dual" Jackson deserialization of a JSON document from a Request Body: one to a Map, and the other to the target POJO. Ideally, I would like to perform this in a single PartialDto<T> instance, where T is my target DTO type.
Better giving an example. Let's say I currently have this PUT mapping in a REST Controller:
#PutMapping("/resource")
public MyDto updateWhole(#RequestBody MyDto dto) {
System.out.println("PUT: updating the whole object to " + dto);
return dto;
}
My idea is to build a PartialDto type that would provide both POJO representation of the request body, as well as the Map representation, like this:
#PatchMapping("/resource")
public MyDto updatePartial(#RequestBody PartialDto<MyDto> partial) {
System.out.println("PATCH: partial update of the object to " + partial);
final MyDto dto = partial.asDto();
// Do stuff using the deserialized POJO
final Map<String, Object> map = partial.asMap();
// Do stuff as a deserialized map...
return dto;
}
I hope this will allow me to further expand the PartialDto implementation so I can perform things like this:
if (partial.hasAttribute("myAttribute")) {
final String myAttribute = dto.getMyAttribute();
// ...
}
Or even using a metamodel generator:
if (partial.hasAttribute(MyDto_.myAttribute)) {
final String myAttribute = dto.getMyAttribute();
// ...
}
So the question is simple: Jackson can easily map a JSON document to a POJO. It can also easily map a JSON document to a java Map. How can I do both at the same time in a Wrapper object such as my PartialDto?
public class PartialDto<T> {
private final Map<String, Object> map;
private final T dto;
PartialDto(Map<String, Object> map, T dto) {
this.map = map;
this.dto = dto;
}
public T asDto() {
return this.dto;
}
public Map<String, Object> asMap() {
return Collections.unmodifiableMap(this.map);
}
}
I tried to use a GenericConverter like this (that, of course, I registered in Spring MVC's FormatterRegistry):
public class PartialDtoConverter implements GenericConverter {
private final ObjectMapper objectMapper;
public PartialDtoConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, PartialDto.class));
}
#Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
final Class<?> targetClazz = targetType.getResolvableType().getGeneric(0).getRawClass();
final Map<String, Object> map;
try {
map = objectMapper.readValue((String) source, Map.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e); // FIXME
}
final Object dto = objectMapper.convertValue(map, targetClazz);
return new PartialDto(map, dto) ;
}
}
And this converter works well when tested directly using Spring's ConversionService:
#SpringBootTest
class ConverterTest {
#Autowired
private ConversionService conversionService;
#Test
public void testPartialUpdate() throws Exception {
final MyDto dto = new MyDto()
.setIt("It");
final PartialDto<MyDto> partialDto = (PartialDto<MyDto>) conversionService.convert(
"{ \"it\": \"Plop\" }",
new TypeDescriptor(ResolvableType.forClass(String.class), null, null),
new TypeDescriptor(ResolvableType.forClassWithGenerics(PartialDto.class, MyDto.class), null, null)
);
Assertions.assertEquals("Plop", partialDto.asDto().getIt());
Assertions.assertEquals("Plop", partialDto.asMap().get("it"));
}
}
However, it doesn't seem to work in a #RequestBody such as shown above. Reminder:
#PatchMapping("/resource")
public MyDto updatePartial(#RequestBody PartialDto<MyDto> partial) {
// ...
}
Any idea is welcome.

Spring-boot MultipartFile issue with ByteArrayResource

I'm trying to implement a rest api consuming excel file. I'm using spring-boot and code is available here.
Code works fine when using FileSystemResource for payload. But i'm not able to make the code work with ByteArrayResource in replacement of FileSystemResource:
RestApi.java:
#RestController
public class RestApi {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
#PostMapping("/api/upload")
public ResponseEntity<?> uploadFile(#RequestParam("file") MultipartFile uploadfile) {
LOGGER.debug("Single file upload!");
try {
LOGGER.info("\n\n ****** File name: {}, type {}! ************", uploadfile.getOriginalFilename(), uploadfile.getContentType());
this.processExcelFile(uploadfile.getInputStream());
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("Successfully uploaded - " + uploadfile.getOriginalFilename(), new HttpHeaders(), HttpStatus.OK);
}
private List<String> processExcelFile(InputStream stream) throws Exception {
List<String> result = new ArrayList<String>();
//Create Workbook instance holding reference to .xlsx file
try(XSSFWorkbook workbook = new XSSFWorkbook(stream);) {
//Get first/desired sheet from the workbook
XSSFSheet sheet = workbook.getSheetAt(0);
//Iterate through each rows one by one
Iterator<Row> rowIterator = sheet.iterator();
while (rowIterator.hasNext()) {
Row row = rowIterator.next();
String cellValue = row.getCell(0).getRichStringCellValue().toString();
result.add(cellValue);
LOGGER.info("\n\n ****** Cell value: {} ************", cellValue);
}
return result;
}
}
}
RestApiTest:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RestApiTest {
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private ResourceLoader loader;
#Test
public void testUploadFile() throws Exception {
Resource resource = this.loader.getResource("classpath:test.xlsx");
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
// parts.add("file", new FileSystemResource(resource.getFile()));
parts.add("file", new ByteArrayResource(IOUtils.toByteArray(resource.getInputStream())));
String response = this.restTemplate.postForObject("/api/upload", parts, String.class);
Assertions.assertThat(response).containsIgnoringCase("success");
}
}
I'm getting following error when running test:
java.lang.AssertionError:
Expecting:
<"{"timestamp":1487852597527,"status":400,"error":"Bad Request","exception":"org.springframework.web.multipart.support.MissingServletRequestPartException","message":"Required request part 'file' is not present","path":"/api/upload"}">
to contain:
<"success">
(ignoring case)
Any idea?
when using loader.getResource(...) you must use resource itself as answered above. So you don't need ByteArrayResource. I got this problem, but I'm not using resource from classpath. So if someone really need to use ByteArrayResource, here is my workaround
public class FileNameAwareByteArrayResource extends ByteArrayResource {
private String fileName;
public FileNameAwareByteArrayResource(String fileName, byte[] byteArray, String description) {
super(byteArray, description);
this.fileName = fileName;
}
#Override
public String getFilename() {
return fileName;
}
}
and then use it
parts.add("file", new FileNameAwareByteArrayResource("filename", byteArray));

Spring implementation of Flyweight pattern

I have a Flyweight pattern implementation in java programming without Spring. Please suggest how do I make this as a Spring managed Bean. It has static method calls and Inner class. The purpose is to initially load all the OptionSets from CRM to share the same OptionSets in the application. This prevents expensive calls to CRM. Users are restricted to create any new OptionSets hence Inner class. First need to implement as a Spring bean and then get it to be ApplicationContextAware to reference other beans. I am primarily using Spring XML configuration for bean definition.
public class OptionSetFactory{
private static Map <String, Object>optionSets = new HashMap();
//Inner class to restrict users creating OptionSet
private class OptionSet implements IOptionSet{
private String entityName;
private String attributeName;
private Hashtable<Integer, String> options;
private IOrganizationService service;
private static final String GUID_EMPTY = "00000000-0000-0000-0000-000000000000";
private ApplicationContext context;
OptionSet(String entityName, String attributeName){
this.entityName = entityName;
this.attributeName = attributeName;
//this.options = options;
OrganizationRequest request = new OrganizationRequest();
request.setRequestName("RetrieveAttribute");
Guid guid = new Guid();
guid.setValue(GUID_EMPTY);
ParameterCollection paramCol = new ParameterCollection();
KeyValuePairOfstringanyType kv0 = new KeyValuePairOfstringanyType();
kv0.setKey("MetadataId");
kv0.setValue(guid);
paramCol.getKeyValuePairOfstringanyTypes().add(kv0);
KeyValuePairOfstringanyType kv1 = new KeyValuePairOfstringanyType();
kv1.setKey("EntityLogicalName");
kv1.setValue(entityName);
paramCol.getKeyValuePairOfstringanyTypes().add(kv1);
KeyValuePairOfstringanyType kv2 = new KeyValuePairOfstringanyType();
kv2.setKey("LogicalName");
kv2.setValue(attributeName);
paramCol.getKeyValuePairOfstringanyTypes().add(kv2);
KeyValuePairOfstringanyType kv3 = new KeyValuePairOfstringanyType();
kv3.setKey("RetrieveAsIfPublished");
kv3.setValue(true);
paramCol.getKeyValuePairOfstringanyTypes().add(kv3);
request.setParameters(paramCol);
try {
OrganizationResponse response=service.execute(request);
PicklistAttributeMetadata pickListAttrMetadata = (PicklistAttributeMetadata)response.getResults().getKeyValuePairOfstringanyTypes().get(0).getValue();
OptionSetMetadata optionSetMetadata = pickListAttrMetadata.getOptionSet();
for(OptionMetadata optionMetaData : optionSetMetadata.getOptions().getOptionMetadatas()){
//TODO populate OptionSet from optionsetMetadata
}
} catch (IOrganizationServiceExecuteOrganizationServiceFaultFaultFaultMessage e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public String getEntityName() {
return entityName;
}
#Override
public String getAttributeName() {
return attributeName;
}
#Override
public Hashtable<Integer, String> getOptions() {
return options;
}
}
//static block to load predefined OptionSets in HashMap
static{
OptionSetFactory factory = new OptionSetFactory();
optionSets.put("dsl_type", factory.new OptionSet("dsl_type", "dsl_operationalstructure"));
}
//Static method calls for user to fetch OptionSet based on inputs
public static IOptionSet getOptionSet(String entityName, String attributeName){
return (IOptionSet) optionSets.get(entityName+"."+attributeName);
}
public static IOptionSet getOptionSet(String attributeName){
return (IOptionSet) optionSets.get(attributeName.toLowerCase());
}
}

Resources