Adding mapper to myBatis configuration in Java - java

I want to migrate from XML configuration to Java configuration.
sqlSessionFactory.getConfiguration().setEnvironment(new Environment("development", new org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory(), getDataSouroce()));
I managed to replace all <environments> section with Java configuration (I have removed <environments> from XML configuration file), but I can't get rid of:
<mappers><mapper resource="mailbox/db/map/FileMapper.xml"/> </mappers>
I tried to write:
sqlSessionFactory.getConfiguration().addMapper(FileMapper.class);
but there are exceptions:
SqlSession sqlSession = MyBatisConnectionFactory.instance.getSqlSessionFactory().openSession();
FileExample fe = new FileExample();
Criteria f = fe.createCriteria().andIdBetween(0L, 5L);
FileMapper mapper = (FileMapper) sqlSession.getMapper(FileMapper.class);
List<File> allRecords = mapper.selectByExample(fe);
// Mapped Statements collection does not contain value for mailbox.db.dao.FileMapper.selectByExample

I am using below abstract mapper factory where DbUtil.getInstance().getDataSource() and registerMappers() are the key points.
public abstract class AbstractMapperFactory implements MapperFactory {
private ThreadLocal<SqlSessionManager> sessionManagerThreadLocal = new ThreadLocal<SqlSessionManager>();
public <T> T getMapper(Class<T> clazz) throws DaoException {
if(sessionManagerThreadLocal.get() == null) {
initialize();
}
return sessionManagerThreadLocal.get().getMapper(clazz);
}
public void closeSession() {
if(sessionManagerThreadLocal.get() != null) {
sessionManagerThreadLocal.get().close();
sessionManagerThreadLocal.remove();
}
}
private void initialize() throws DaoException {
Environment environment = new Environment("env", new ManagedTransactionFactory(), DbUtil.getInstance().getDataSource());
Configuration configuration = new Configuration(environment);
registerMappers(configuration);
sessionManagerThreadLocal.set(SqlSessionManager.newInstance(new SqlSessionFactoryBuilder().build(configuration)));
}
protected abstract void registerMappers(Configuration configuration);
}
Where DbUtil.getInstance().getDataSource() is responsible for getting the java.sql.DataSource instance, whether it is managed or simple.
registerMappers() is an abstract method where subclass can register their mappers using code like below:
protected void registerMappers(Configuration configuration) {
configuration.addMapper(PartMapper.class);
configuration.addMapper(StatusMapper.class);
configuration.addMapper(NoteTypeMapper.class);
configuration.addMapper(AssetTypeMapper.class);
}

Related

Refactor polymorphism using Java 8

I have an old code base that I need to refactor using Java 8, so I have an interface, which tells whether my current site supports the platform.
public interface PlatformSupportHandler {
public abstract boolean isPaltformSupported(String platform);
}
and I have multiple classes implementing it and each class supports a different platform.
A few of the implementing classes are:
#Component("bsafePlatformSupportHandler")
public class BsafePlatoformSupportHandler implements PlatformSupportHandler {
String[] supportedPlatform = {"iPad", "Android", "iPhone"};
Set<String> supportedPlatformSet = new HashSet<>(Arrays.asList(supportedPlatform));
#Override
public boolean isPaltformSupported(String platform) {
return supportedPlatformSet.contains(platform);
}
}
Another implementation:
#Component("discountPlatformSupportHandler")
public class DiscountPlatoformSupportHandler implements PlatformSupportHandler{
String[] supportedPlatform = {"Android", "iPhone"};
Set<String> supportedPlatformSet = new HashSet<>(Arrays.asList(supportedPlatform));
#Override
public boolean isPaltformSupported(String platform) {
return supportedPlatformSet.contains(platform);
}
}
At runtime in my filter, I get the required bean which I want:
platformSupportHandler = (PlatformSupportHandler) ApplicationContextUtil
.getBean(subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND);
and call isPlatformSupported to get whether my current site supports the following platform or not.
I am new to Java 8, so is there any way I can refactor this code without creating multiple classes? As the interface only contains one method, can I somehow use lambda to refactor it?
If you want to stick to the current design, you could do something like this:
public class MyGeneralPurposeSupportHandler implements PlatformSupportHandler {
private final Set<String> supportedPlatforms;
public MyGeneralPurposeSupportHandler(Set<String> supportedPlatforms) {
this.supportedPlatforms = supportedPlatforms;
}
public boolean isPlatformSupported(String platform) {
return supportedPlatforms.contains(platform);
}
}
// now in configuration:
#Configuration
class MySpringConfig {
#Bean
#Qualifier("discountPlatformSupportHandler")
public PlatformSupportHandler discountPlatformSupportHandler() {
return new MyGeneralPurposeSupportHandler(new HashSefOf({"Android", "iPhone"})); // yeah its not a java syntax, but you get the idea
}
#Bean
#Qualifier("bsafePlatformSupportHandler")
public PlatformSupportHandler bsafePlatformSupportHandler() {
return new MyGeneralPurposeSupportHandler(new HashSefOf({"Android", "iPhone", "iPad"}));
}
}
This method has an advantage of not creating class per type (discount, bsafe, etc), so this answers the question.
Going step further, what happens if there no type that was requested, currently it will fail because the bean does not exist in the application context - not a really good approach.
So you could create a map of type to the set of supported platforms, maintain the map in the configuration or something an let spring do its magic.
You'll end up with something like this:
public class SupportHandler {
private final Map<String, Set<String>> platformTypeToSuportedPlatforms;
public SupportHandler(Map<String, Set<String>> map) {
this.platformTypeToSupportedPlatforms = map;
}
public boolean isPaltformSupported(String type) {
Set<String> supportedPlatforms = platformTypeToSupportedPlatforms.get(type);
if(supportedPlatforms == null) {
return false; // or maybe throw an exception, the point is that you don't deal with spring here which is good since spring shouldn't interfere with your business code
}
return supportedPlatforms.contains(type);
}
}
#Configuration
public class MyConfiguration {
// Configuration conf is supposed to be your own way to read configurations in the project - so you'll have to implement it somehow
#Bean
public SupportHandler supportHandler(Configuration conf) {
return new SupportHandler(conf.getRequiredMap());
}
}
Now if you follow this approach, adding a new supported types becomes codeless at all, you only add a configuration, by far its the best method I can offer.
Both methods however lack the java 8 features though ;)
You can use the following in your config class where you can create beans:
#Configuration
public class AppConfiguration {
#Bean(name = "discountPlatformSupportHandler")
public PlatformSupportHandler discountPlatformSupportHandler() {
String[] supportedPlatforms = {"Android", "iPhone"};
return getPlatformSupportHandler(supportedPlatforms);
}
#Bean(name = "bsafePlatformSupportHandler")
public PlatformSupportHandler bsafePlatformSupportHandler() {
String[] supportedPlatforms = {"iPad", "Android", "iPhone"};
return getPlatformSupportHandler(supportedPlatforms);
}
private PlatformSupportHandler getPlatformSupportHandler(String[] supportedPlatforms) {
return platform -> Arrays.asList(supportedPlatforms).contains(platform);
}
}
Also, when you want to use the bean, it is again very easy:
#Component
class PlatformSupport {
// map of bean name vs bean, automatically created by Spring for you
private final Map<String, PlatformSupportHandler> platformSupportHandlers;
#Autowired // Constructor injection
public PlatformSupport(Map<String, PlatformSupportHandler> platformSupportHandlers) {
this.platformSupportHandlers = platformSupportHandlers;
}
public void method1(String subProductType) {
PlatformSupportHandler platformSupportHandler = platformSupportHandlers.get(subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND);
}
}
As it was written in Mark Bramnik's answer you can move this to configuration.
Suppose that it would be in yaml in that way:
platforms:
bsafePlatformSupportHandler: ["iPad", "Android", "iPhone"]
discountPlatformSupportHandler: ["Android", "iPhone"]
Then you can create config class to read this:
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties
public class Config {
private Map<String, List<String>> platforms = new HashMap<String, List<String>>();
// getters and setters
You can than create handler with checking code.
Or place it in your filter like below:
#Autowired
private Config config;
...
public boolean isPlatformSupported(String subProductType, String platform) {
String key = subProductType + Constants.PLATFORM_SUPPORT_HANDLER_APPEND;
return config.getPlatforms()
.getOrDefault(key, Collections.emptyList())
.contains(platform);
}

Spring. How to inject a dependence according on application arguments?

I am new in Spring framework. I develop a standalone console application. App will get several files of different format ( CSV, JSP, XML) as arguments. I want inject a certain implementation of parser according to file format.
my service and parsers
These is my service:
#Service
public class ParsingService {
private final Parser parser;
#Autowired
public ParsingService(Parser parser) {
this.parser = parser;
}
public List<Order> parse(String filePath) {
try {
return parser.parse(filePath);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
My main class:
public class Main {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConf.class);
for (String arg : args) {
ParsingService service = context.getBean(ParsingService.class);
List<Order> listOfParsedObjects = service.parse(arg);
listOfParsedObjects.forEach(System.out::println);
}
}
}
I will pass to command line several file paths and i need Spring to inject necessary implementation depending on file format.
Assuming that Parser is your own interface you can add a method telling the format it's able to parse:
public interface Parser {
List<Order> parse(String filePath);
String getFormat();
}
Then override it in all the implementations:
#Component
public class CsvParser implements Parser {
public static final String FORMAT = "csv";
public String getFormat(){
return FORMAT;
}
// ...
}
Configure your parser beans either by annotating the classes with #Bean/#Component or by creating instances in your config class. (If you're using SpringBoot I would suggest using #ConditionalOn... annotations in order to avoid creation of unnecessary beans)
Now you can inject all of your Parser instances into ParserService.
#Service
public class ParsingService {
private final Map<String, Parser> parsers;
#Autowired
public ParsingService(List<Parser> allParsers) {
this.parsers = allParsers
.stream()
.collect(Collectors.toMap(Parser::getFormat, p -> p));
}
public List<Order> parse(String filePath) {
try {
String format = getFormat(filePath);
Parser parser = parsers.get(format);
if(parser == null) {
// Replace this exception by a more appropriate one
throw new RuntimeException("No parsers found for format : " + format);
} else {
return parser.parse(filePath);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private String getFormat(String filePath){
int i = filePath.lastIndexOf('.');
if (i > 0) {
return filePath.substring(i+1).toLowerCase();
} else {
// Replace this exception by a more appropriate one
throw new RuntimeException("Cannot determine the file format!");
}
}
}
This way neither your ParserService nor Main classes will depend from your custom Parser implementations. Once you need a new parser you can simply define a new class implementing the interface. No more changes needed.
UPDATE
Adding Main and AppConfig classes
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConf.class);
ParsingService service = context.getBean(ParsingService.class);
for (String arg : args) {
List<Order> listOfParsedObjects = service.parse(arg);
listOfParsedObjects.forEach(System.out::println);
}
}
}
#Configuration
#ComponentScan(basePackages = "your.root.package")
public class AppConf {
// Do something here
}
For parallel processing try replacing your for-loop in Main with the following code:
Arrays.stream(args)
.parallel()
.map(service::parse)
.flatMap(List::stream)
.forEach(System.out::println);
Or you can use an ExecutorService:
int poolSize = 3;
ExecutorService executorService = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
for (String arg : args) {
executorService.submit(() -> {
service.parse(arg).forEach(System.out::println);
});
}
My recommendation would be to consider using Spring Boot and the #ConditionalOnProperty annotation. In the code example below, there will only ever be a bean called csvParserImpl if the property of my.parser has the value of csv. By changing the property value from csv to json, jsonParserImpl will be created instead of csvParserImpl. If my.parser is not defined or set to a value which doesn't include neither csv nor json, then there will be no instance of Parser.
#Configuration
public class MyAutoconfiguration {
#Bean
#ConditionalOnProperty(name="my.parser", havingValue="csv")
CsvParserImpl csvParserImpl() {
return new CsvParserImpl();
}
#Bean
#ConditionalOnProperty(name="my.parser", havingValue="json")
JsonParserImpl jsonParserImpl() {
return new JsonParserImpl();
}
}
When I'm referring to "property", that has a specific meaning within spring boot. Externalized Configuration in spring boot can pull in property values from a multiple of sources, including environment variables, system variables, and command line variables.
You may want to inject a collection of Parsers
#Autowired
private List<Parser> parsers;
And then choose the correct parser from that list.
Also, that is possible to do through a Map
Spring Annotations - Injecting Map of Objects
You can define the method in the parser interface, that returns a collection of extensions, like this
public interface Parser {
List<String> getExtensions();
}
Then you can utilize Java 8 streams for looking for correct parser:
parsers.stream().filter(p->p.getExtensions().contains(extension)).findFirst();
This will return the optional which may contain the needed parser
When you add a parser, what you need is to add a parser and define the extensions. No need to change the code in main

Is it possible to create integration flow beans from different property values by using BeanFactory?

My intention is to create IntegrationFlow bean instances from various sources/directories(for the first place, later maybe from ftp). Therefore in the application.properties I'd like to define something like this, the number of inbound directories may vary:
inbound.file.readPath[0]=source1
inbound.file.processedPath[0]=processed1
inbound.file.failedPath[0]=failed1
inbound.file.readPath[1]=source2
inbound.file.processedPath[1]=processed2
inbound.file.failedPath[1]=failed2
I'd also like to maintain the origin of the sources (via header enrichment) so it is not an option to put all files into one directory outside from spring.
So having a FilePollingFlow is it possible to create these bean instances from the above mentioned properties? I could imagine something like this, but I am not sure how to pass the properties to the bean instances and how to reference the indices:
#Configuration
public class FilePollingIntegrationFlow extends AbstractFactoryBean<IntegrationFlow> {
#Autowired
private FilePollingConfiguration config;
#Override
public Class<IntegrationFlow> getObjectType() {
return IntegrationFlow.class;
}
#Override
protected IntegrationFlow createInstance() throws Exception {
return IntegrationFlows
.from(s -> /* FIXME config.getReadPath()? instead of inboundReadDirectory, but how to handle indices? */s.file(inboundReadDirectory).preventDuplicates(true).scanEachPoll(true).patternFilter("*.txt"),
e -> e.poller(Pollers.fixedDelay(inboundPollingPeriod)
.taskExecutor(taskExecutor())
.transactionSynchronizationFactory(transactionSynchronizationFactory())
.transactional(transactionManager())))
.log(LoggingHandler.Level.INFO, getClass().getName(), "'Read inbound file: ' .concat(payload)")
.enrichHeaders(m -> m.headerExpression(FileHeaders.ORIGINAL_FILE, "payload"))
.transform(Transformers.fileToString())
.channel(ApplicationConfiguration.FILE_INBOUND_CHANNEL)
.get();
}
}
#Component
#ConfigurationProperties("inbound")
public class FilePollingConfiguration {
private List<File> files = new ArrayList<>();
public static class File {
private String readPath;
private String processedPath;
private String failedPath;
public String getReadPath() {
return readPath;
}
public void setReadPath(String readPath) {
this.readPath = readPath;
}
public String getProcessedPath() {
return processedPath;
}
public void setProcessedPath(String processedPath) {
this.processedPath = processedPath;
}
public String getFailedPath() {
return failedPath;
}
public void setFailedPath(String failedPath) {
this.failedPath = failedPath;
}
#Override
public String toString() {
return new ToStringBuilder(this)
.append("readPath", readPath)
.append("processedPath", processedPath)
.append("failedPath", failedPath)
.toString();
}
public List<File> getFiles() {
return files;
}
public void setFiles(List<File> files) {
this.files = files;
}
}
For the several similar flows the Framework provides for you a solution like IntegrationFlowContext: https://docs.spring.io/spring-integration/docs/5.0.3.RELEASE/reference/html/java-dsl.html#java-dsl-runtime-flows. So, what you need is just an iteration over those files and creation flows on the fly and their registration.
Regarding list of similar properties like you inner File you should revise the recommendations from Spring Boot: https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/htmlsingle/#boot-features-external-config-yaml.
Pay attention to the servers property there. I mean if you call it files in the Java class, that has to be files in the properties file.
UPDATE
This is how it works for me:
my application.properties
my.source.dirs=/tmp/in1,/tmp/in2
The app is like this:
#SpringBootApplication
public class So49168720Application {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(So49168720Application.class, args);
File file1 = new File("/tmp/in1", "foo.txt");
file1.createNewFile();
FileCopyUtils.copy("FOO".getBytes(), file1);
File file2 = new File("/tmp/in2", "bar.txt");
file2.createNewFile();
FileCopyUtils.copy("BAR".getBytes(), file2);
PollableChannel resultChannel = applicationContext.getBean("resultChannel", PollableChannel.class);
System.out.println(resultChannel.receive(10000));
System.out.println(resultChannel.receive(10000));
file1.delete();
file2.delete();
}
#Value("${my.source.dirs}")
private String[] sourceDirs;
#Autowired
private IntegrationFlowContext flowContext;
#PostConstruct
private void registerFilePollingFlows() {
Arrays.asList(this.sourceDirs).forEach(inboundSource -> {
IntegrationFlow flow =
IntegrationFlows
.from(Files.inboundAdapter(new File(inboundSource))
.patternFilter("*.txt"))
.log(LoggingHandler.Level.INFO, getClass().getName(),
"'Read inbound file: ' .concat(payload)")
.transform(Files.toStringTransformer())
.channel(resultChannel())
.get();
this.flowContext.registration(flow).register();
});
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerSpec defaultPoller() {
return Pollers.fixedDelay(1000);
}
#Bean
public PollableChannel resultChannel() {
return new QueueChannel();
}
}
And I have in the logs these messages:
2018-03-13 17:43:05.148 INFO 19676 --- [ask-scheduler-3] ication$$EnhancerBySpringCGLIB$$4fda2b12 : Read inbound file: \tmp\in2\bar.txt
2018-03-13 17:43:05.148 INFO 19676 --- [ask-scheduler-2] ication$$EnhancerBySpringCGLIB$$4fda2b12 : Read inbound file: \tmp\in1\foo.txt
GenericMessage [payload=BAR, headers={file_originalFile=\tmp\in2\bar.txt, id=4a692a68-3871-b708-a28e-c4dc378de7e5, file_name=bar.txt, file_relativePath=bar.txt, timestamp=1520977385150}]
GenericMessage [payload=FOO, headers={file_originalFile=\tmp\in1\foo.txt, id=32597359-6602-3df6-5f6f-dac2f4ad788f, file_name=foo.txt, file_relativePath=foo.txt, timestamp=1520977385150}]
However this is already based on the Spring Integration 5.0 and Spring Boot 2.0. Any reason do not upgrade your project ?

#JsonView fails with 'IllegalStateException Can not override serializer' when using Spring repository

We're using some Spring (v4.1.3.RELEASE) repositories and lately started using #JsonView to filter some responses data with other Controllers we have in our system.
We've discovered today that jackson tries and fail to swap serializers for unknown reason of properties flagged with the #JsonView annotation.
Debugging led us to BeanSerializerBase.resolve(..) = lines #333-#337 where the assignment is made and fails later with the exception 'IllegalStateException Can not override serializer'. There is also a reference in the code to [JACKSON-364].
Removing all JsonView annotations fixed it as a workaround.
We're still trying to debug and nail the root cause of it but any hint will be appreciated here.
Thanks!
We can see in source code
com.fasterxml.jackson.databind.ser.BeanPropertyWriter
/**
* Method called to assign value serializer for property
*
* #since 2.0
*/
public void assignSerializer(JsonSerializer<Object> ser)
{
// may need to disable check in future?
if (_serializer != null && _serializer != ser) {
throw new IllegalStateException("Can not override serializer");
}
_serializer = ser;
}
So we can't change serializer dynamicly with this writer class.
To hide this problem we can use (for example)
public class ExtendBeanPropertyWriter extends BeanPropertyWriter {
// constructors
#Override
public BeanPropertyWriter unwrappingWriter(NameTransformer unwrapper) {
return new ExtendUnwrappingBeanPropertyWriter(this, unwrapper);
}
#Override
public void assignSerializer(JsonSerializer<Object> ser) {
_serializer = ser;
}
}
And we have to remember about UnwrappedBeanPropertyWriter
public class ExtendUnwrappingBeanPropertyWriter extends UnwrappingBeanPropertyWriter {
// constructors
#Override
public void assignSerializer(JsonSerializer<Object> ser) {
_serializer = ser;
}
}
Then we can extend BeanSerializerModifier, method - changeProperties, to change writer's classes to our implementations and avoid this exception.
public class ExtendBeanSerializerModifier extends BeanSerializerModifier {
private AnnotationIntrospector annotationIntrospector;
// constructors
#Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
annotationIntrospector = null == config ? null : config.getAnnotationIntrospector();
List<BeanPropertyWriter> result = new ArrayList<BeanPropertyWriter>();
for (BeanPropertyWriter beanPropertyWriter : beanProperties) {
BeanPropertyWriter bpw = beanPropertyWriter;
// we can get views from bpw.getViews(); active view from config.getActiveView();
// we also can use own annotations, for example - beanDesc.getBeanClass().getAnnotation(SomeClass.class);
result.add(getExtendBPW(bpw));
}
return result;
}
public BeanPropertyWriter getExtendBPW(BeanPropertyWriter bpw) {
BeanPropertyWriter writer = new ExtendBeanPropertyWriter(bpw);
if (null != annotationIntrospector) {
NameTransformer unwrapper = annotationIntrospector.findUnwrappingNameTransformer(bpw.getMember());
if (null != unwrapper) {
writer = writer.unwrappingWriter(unwrapper);
}
}
return writer;
}
}
And even after that you can get a problems with JsonView.
You also maybe have to add your own HttpMessageConverter and/or method Interceptor (you can find examples in internet).

JAXB and Guice: How to integrate and visualize?

I find using JAXB together with Guice possible, but challenging: Both libraries "fight" for control over object creation, you have to be careful to avoid cyclic dependencies, and it can get messy with all the JAXB Adapters and Guice Providers and stuff. My questions are:
How do you deal with this configuration? What general strategies / rules of thumb can be applied?
Can you point me to a good tutorial or well written sample code?
How to visualize the dependencies (including the Adapters and Providers)?
For some sample code, some example work was done here: http://jersey.576304.n2.nabble.com/Injecting-JAXBContextProvider-Contextprovider-lt-JAXBContext-gt-with-Guice-td5183058.html
At the line that says "Wrong?", put in the recommended line.
I looks like this:
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = { UserBasic.class, UserBasicInformation.class };
public JAXBContextResolver() throws Exception {
this.context =
new JSONJAXBContext(
JSONConfiguration.natural().build(), types);
}
public JAXBContext getContext(Class<?> objectType) {
/*
for (Class type : types) {
if (type == objectType) {
return context;
}
} // There should be some kind of exception for the wrong type.
*/
return context;
}
}
//My resource method:
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public JAXBElement<UserBasic> get(#QueryParam("userName") String userName) {
ObjectFactory ob = new ObjectFactory();
UserDTO dto = getUserService().getByUsername(userName);
if(dto==null) throw new NotFoundException();
UserBasic ub = new UserBasic();
ub.setId(dto.getId());
ub.setEmailAddress(dto.getEmailAddress());
ub.setName(dto.getName());
ub.setPhoneNumber(dto.getPhoneNumber());
return ob.createUserBasic(ub);
}
//My Guice configuration module:
public class MyServletModule extends ServletModule {
public static Module[] getRequiredModules() {
return new Module[] {
new MyServletModule(),
new ServiceModule(),
new CaptchaModule()
};
}
#Override
protected void configureServlets() {
bind(UserHttpResource.class);
bind(JAXBContextResolver.class);
serve("/*").with(GuiceContainer.class);
}
}

Categories

Resources