All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.atlasmap.service.AtlasService Maven / Gradle / Ivy

/**
 * Copyright (C) 2017 Red Hat, Inc.
 *
 * 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 io.atlasmap.service;

import io.atlasmap.api.AtlasContext;
import io.atlasmap.api.AtlasConverter;
import io.atlasmap.api.AtlasConversionService;
import io.atlasmap.api.AtlasException;
import io.atlasmap.api.AtlasSession;
import io.atlasmap.core.DefaultAtlasContextFactory;
import io.atlasmap.java.v2.JavaField;
import io.atlasmap.v2.ActionDetails;
import io.atlasmap.v2.AtlasMapping;
import io.atlasmap.v2.Field;
import io.atlasmap.v2.FieldType;
import io.atlasmap.v2.Mapping;
import io.atlasmap.v2.StringMap;
import io.atlasmap.v2.StringMapEntry;
import io.atlasmap.v2.Validations;

import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@ApplicationPath("/")
@Path("v2/atlas")
public class AtlasService extends Application {		
	
    private static final Logger logger = LoggerFactory.getLogger(AtlasService.class);
	final Application javaServiceApp;
	final DefaultAtlasContextFactory atlasContextFactory = DefaultAtlasContextFactory.getInstance();
	private String baseFolder = "target/mappings";
	
	private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
	private static final String DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN = "*";
	private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
	private static final String DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS = "Content-Type";
    private static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
    private static final String DEFAULT_ACCESS_CONTROL_ALLOW_METHODS = "GET,PUT,POST,PATCH,DELETE,OPTIONS,HEAD";
    private static final String ACCESS_CONTROL_ALLOW_METHODS_GPPPD = "GET,PUT,POST,PATCH,DELETE";

	
	public AtlasService() {
		javaServiceApp = new ResourceConfig().register(JacksonFeature.class);
	}

	protected Response standardCORSResponse() {
	    return Response.ok()
                .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                .header(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ACCESS_CONTROL_ALLOW_METHODS)
                .build();
	}
	
    @OPTIONS
    @Path("/mapping")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getMappingOptions() {
    	return standardCORSResponse();
    }
    
    @OPTIONS
    @Path("/mapping/validate")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getMappingValidateOptions() {
        return standardCORSResponse();
    }
    
    @OPTIONS
    @Path("/mapping/validate/{mappingId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getMappingValidateParameterizedOptions() {
        return standardCORSResponse();
    }
    
    @OPTIONS
    @Path("/fieldMapping/converterCheck")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getFieldMappingConverterCheckOptions() {
        return standardCORSResponse();
    }
     
    @OPTIONS
    @Path("/mapping/{mappingId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getMappingParameterizedOptions() {
        return standardCORSResponse();
    }

    @OPTIONS
    @Path("/mappings")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getMappingsOptions() {
        return standardCORSResponse();
    }
    
    @OPTIONS
    @Path("/fieldActions")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getFieldActionsOptions() {
        return standardCORSResponse();
    }
	
    @GET
    @Path("/fieldActions")
    @Produces(MediaType.APPLICATION_JSON)
    public Response listFieldActions(@Context UriInfo uriInfo) {
        ActionDetails details = new ActionDetails();
        
        if(atlasContextFactory == null || atlasContextFactory.getFieldActionService() == null) {
            return Response.ok()
                    .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                    .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                    .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
                    .entity(details)
                    .build();
        }
        
        details.getActionDetail().addAll(atlasContextFactory.getFieldActionService().listActionDetails());
        return Response.ok()
                .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
                .entity(details)
                .build();
    }

    
    @GET
    @Path("/mappings")
    @Produces(MediaType.APPLICATION_JSON)
    public Response listMappings(@Context UriInfo uriInfo, final @QueryParam("filter") String filter) {
    	StringMap sMap = new StringMap();
    	
    	java.nio.file.Path mappingFolder = Paths.get(baseFolder);
    	File[] mappings = mappingFolder.toFile().listFiles(new FilenameFilter() {
			
			@Override
			public boolean accept(File dir, String name) {
				if (filter != null && name != null && !name.toLowerCase().contains(filter.toLowerCase())) {
					return false;
				}
                return (name != null ? name.matches("atlasmapping-[a-zA-Z0-9\\.\\-]+.xml") : false);
            }
		});
    	
    	if(mappings == null) {
    		return Response.ok()
                .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
        			.entity(sMap)
        			.build();
    	}
    	
    	try {
    	    for(File mapping : mappings) {
    	        AtlasMapping map = getMappingFromFile(mapping.getAbsolutePath());
    	        StringMapEntry mapEntry = new StringMapEntry();
    	        mapEntry.setName(map.getName());
    		
    	        UriBuilder builder = uriInfo.getBaseUriBuilder()
        			.path("v2").path("atlas").path("mapping").path(map.getName());
    	        mapEntry.setValue(builder.build().toString());
    	        sMap.getStringMapEntry().add(mapEntry);
    	    }
    	} catch (JAXBException e) {
            throw new WebApplicationException(e.getMessage(), Status.INTERNAL_SERVER_ERROR);
    	}
    	    	
    	return Response.ok()
            .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
            .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
            .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
    			.entity(sMap)
    			.build();
    }      
    
    @DELETE
    @Path("/mapping/{mappingId}")
    @Produces(MediaType.APPLICATION_JSON)    
    public Response removeMappingRequest(@PathParam("mappingId") String mappingId) {
    	
    	java.nio.file.Path mappingFilePath = Paths.get(baseFolder + File.separator + generateMappingFileName(mappingId));
    	File mappingFile = mappingFilePath.toFile();
    	
    	if(!mappingFile.exists()) {
    		return Response.noContent()
                 .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                 .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                 .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
        			.build();
    	}
    	
    	if(mappingFile != null && !mappingFile.delete()) {
        logger.warn("Unable to delete mapping file " + mappingFile.toString());
    }
    	    	
    	return Response.ok()
            .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
            .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
            .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
    			.build();
    }
    
    @GET
    @Path("/mapping/{mappingId}")
    @Produces(MediaType.APPLICATION_JSON)    
    public Response getMappingRequest(@PathParam("mappingId") String mappingId) {
    	
    	java.nio.file.Path mappingFilePath = Paths.get(baseFolder + File.separator + generateMappingFileName(mappingId));
    	File mappingFile = mappingFilePath.toFile();
    	
    	if(!mappingFile.exists()) {
    		return Response.noContent()
                .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
        			.build();
    	}
    	
    	AtlasMapping atlasMapping = null;
    	try {
    		atlasMapping = getMappingFromFile(mappingFile.getAbsolutePath());
    	} catch (Exception e) {
    		logger.error("Error retrieving mapping " + e.getMessage(), e);
    	}
    	
    	return Response.ok()
            .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
            .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
            .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
    			.entity(atlasMapping)
    			.build();
    }
    
    @PUT
    @Path("/mapping")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response createMappingRequest(AtlasMapping mapping, @Context UriInfo uriInfo) {
    	return saveMapping(mapping, uriInfo);
    }
    
    @POST
    @Path("/mapping/{mappingId}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response updateMappingRequest(AtlasMapping mapping, @Context UriInfo uriInfo) {
    	return saveMapping(mapping, uriInfo);
    }
    	    
    @PUT
    @Path("/mapping/validate")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response validateMappingRequest(AtlasMapping mapping, @Context UriInfo uriInfo) {
        try {
            return validateMapping(mapping, uriInfo);
        } catch (AtlasException | IOException e) {
            throw new WebApplicationException(e.getMessage(), Status.INTERNAL_SERVER_ERROR);
        }
    }
    
    @PUT
    @Path("/fieldMapping/converterCheck")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response processConverterCheckRequest(Mapping mapping, @Context UriInfo uriInfo) {
        return converterCheck(mapping, uriInfo);
    }
    
    protected Response validateMapping(AtlasMapping mapping, UriInfo uriInfo) throws IOException, AtlasException {
        
        File temporaryMappingFile = File.createTempFile("atlas-mapping", "xml");
        temporaryMappingFile.deleteOnExit();
        atlasContextFactory.getMappingService().saveMappingAsFile(mapping, temporaryMappingFile);
        
        AtlasContext context = atlasContextFactory.createContext(temporaryMappingFile.toURI());
        AtlasSession session = context.createSession();
        context.processValidation(session);
        Validations validations = session.getValidations();
        
        if(session.getValidations() == null) {
            validations = new Validations();
        }

        if(temporaryMappingFile.exists() && !temporaryMappingFile.delete()) {
            logger.warn("Failed to deleting temporary file: " + (temporaryMappingFile != null ? temporaryMappingFile.toString() : null));
        }
        
        return Response.ok()
                .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
                .entity(validations)
                .build();
    }
    
    protected Response converterCheck(Mapping mapping, UriInfo uriInfo) {
        
        if(mapping == null) {
            throw new WebApplicationException("Mapping must be specified", Status.BAD_REQUEST);
        }
        
        AtlasConversionService conversionService = atlasContextFactory.getConversionService();

        Validations validations = new Validations();
        List inputFields = new ArrayList();
        List outputFields = new ArrayList();
        
    
        for(Field f : mapping.getInputField()) {
            inputFields.add(f);
        }
        
        for(Field f : mapping.getOutputField()) {
            inputFields.add(f);
        }
        
        //if(mapping.get)
//            throw new AtlasException("Unsupported mapping type: " + mapping.getClass().getName());
        //}
        
        if(inputFields == null || inputFields.isEmpty() || outputFields == null || outputFields.isEmpty()) {
            throw new WebApplicationException("Must have one of inputField(s) and outputField(s) in order to check for available converter", Status.BAD_REQUEST);
        }
        

        FieldType inputType = null;
        FieldType outputType = null;
        
        for(Field inputField : inputFields) {
            if(inputField instanceof JavaField) {
                inputType = ((JavaField)inputField).getFieldType();
            }
            
            for(Field outputField : outputFields) {
                if(outputField instanceof JavaField) {
                    outputType = ((JavaField)outputField).getFieldType();
                }
                
                Optional optionalConverter = conversionService.findMatchingConverter(inputType, outputType);
                if(optionalConverter.isPresent()) {
                    AtlasConverter converter = optionalConverter.get();
                    // TODO: return "ok"
                } else {
                    // TODO: return "Converter needed"
                }
                
            }
        }

        return Response.ok()
                .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
                .entity(validations)
                .build();
    }
    
    protected Response saveMapping(AtlasMapping mapping, UriInfo uriInfo) {
        try {
            saveMappingToFile(mapping);
        } catch (Exception e) {
            logger.error("Error saving mapping " + mapping.getName() + " to file: " + e.getMessage(), e);
        }
        
        UriBuilder builder = uriInfo.getAbsolutePathBuilder();
        builder.path(mapping.getName());

        return Response.ok()
                .header(ACCESS_CONTROL_ALLOW_ORIGIN, DEFAULT_ACCESS_CONTROL_ALLOW_ORIGIN)
                .header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ACCESS_CONTROL_ALLOW_HEADERS)
                .header(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_GPPPD)
                .location(builder.build())
                .build();
    }
    
    public AtlasMapping getMappingFromFile(String fileName) throws JAXBException {
    	    JAXBContext jaxbContext = JAXBContext.newInstance("io.atlasmap.v2:io.atlasmap.java.v2:io.atlasmap.xml.v2:io.atlasmap.json.v2");
    	    Marshaller marshaller = null;
    	    Unmarshaller unmarshaller = null;

		marshaller = jaxbContext.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		unmarshaller = jaxbContext.createUnmarshaller();
		
		StreamSource fileSource = new StreamSource(new File(fileName));
		JAXBElement mappingElem = unmarshaller.unmarshal(fileSource, AtlasMapping.class);
		if(mappingElem != null) {
			return mappingElem.getValue();
		}
		return null;	
    }
    
    protected void saveMappingToFile(AtlasMapping atlasMapping) throws JAXBException {
    	    JAXBContext jaxbContext = JAXBContext.newInstance("io.atlasmap.v2:io.atlasmap.java.v2:io.atlasmap.xml.v2:io.atlasmap.json.v2");
    	    Marshaller marshaller = null;

		marshaller = jaxbContext.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		
		marshaller.marshal(atlasMapping, new File(baseFolder + File.separator + generateMappingFileName(atlasMapping.getName())));
    }
    
    protected String generateMappingFileName(String mappingName) {
    	    return String.format("atlasmapping-%s.xml", mappingName);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy