Rest api development: getting error: MessageBodyWriter not found for media type=application/xml - api

I am new to developing REST api, and trying to create a small dummy REST API.
I am using tomcat version 8.5.X. Eclipse Version: 2019-12 (4.14.0).
Below is my code.
MessageResuroce.java:
package org.vaibhavc.practice.microservice.messanger.resources;
import java.util.List;
import org.vaibhavc.practice.microservice.messanger.model.Message;
import org.vaibhavc.practice.microservice.messanger.service.MessageService;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.GenericEntity;
#Path("/messages")
public class MessageResource {
MessageService messageService = new MessageService();
#GET
#Produces(MediaType.APPLICATION_XML)
public Response getMessage() {
List<Message> newMessage = messageService.getAllMessages();
GenericEntity<List<Message>> list = new GenericEntity<List<Message>>(newMessage) {};
return Response.ok(list).build();
}
}
Message.java
package org.vaibhavc.practice.microservice.messanger.model;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Message {
private long id;
private String message;
private Date created;
private String author;
public Message(){
}
public Message(long id, String message, String author) {
this.id = id;
this.message = message;
this.created = new Date();
this.author = author;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
MessageService.java
package org.vaibhavc.practice.microservice.messanger.service;
import java.util.ArrayList;
import java.util.List;
import org.vaibhavc.practice.microservice.messanger.model.Message;
public class MessageService {
public List<Message> getAllMessages(){
Message m1 = new Message(1L,"Hello world!","Vaibhav");
Message m2 = new Message(2L,"Hello jersery!","Vaibhav");
List<Message> list = new ArrayList<>();
list.add(m1);
list.add(m2);
return list;
}
}
code built and compiled successfully.
When I trying to access url "http://localhost:8080/messanger/webapi/messages" I am getting error as follow:
SEVERE: MessageBodyWriter not found for media type=application/xml, type=class java.util.ArrayList, genericType=java.util.List.
Any idea what I am missing here?

Please add jersey-media-moxy library in your class path and check
Maven Code Snippet
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.31</version>

Related

Value Dependent Deserialization with Jackson

I want to deserialize into a data structure. Dependent on the version of the JSON data I want to deserialize into different implementations of the same interface. And this works so far with a custom deserializer.
However, in the data structure I use references. And I expect that when undefined references are encountered an exception is thrown. The way I programmed it, this does not work together with the interface.
I created a small example with a (currently not passing) test case to show the desired behavior.
Additional Information:
In the test case, when I use concrete classes (instead of the interface) in readValue the desired behavior occurs. That is, when I write mapper.readValue(buggy, Database2.class); instead of mapper.readValue(buggy, DatabaseI.class);. But then I lose the ability to abstract from the particular content of the JSON data.
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.btc.adt.pop.scen.objectstreams.Person;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.IntNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
public class Example {
#Test
public void test() throws JsonProcessingException {
ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
SimpleModule module = new SimpleModule();
module.addDeserializer(DatabaseI.class, new ToyDeserializer());
mapper.registerModule(module);
String correct = "{'version':1,'people':[{'id':'a','friends':['b','c']},{'id':'b','friends':['c']},{'id':'c','friends':['b']}]}";
DatabaseI deserCorrect = mapper.readValue(correct, DatabaseI.class);
System.out.println(mapper.writeValueAsString(deserCorrect));
String buggy = "{'version':2,'people':[{'id':'a','friends':['b','c']},{'id':'b','friends':['c']},{'id':'c','friends':['FOO']}]}";
assertThrows(Exception.class, () -> {
mapper.readValue(buggy, DatabaseI.class);
}, "The reference FOO is undefined. An Exception should be thrown.");
}
}
class Person {
#JsonProperty("id")
private String id;
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#JsonIdentityReference(alwaysAsId = true)
private List<Person> friends = new ArrayList<>();
public Person() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Person> getFriends() {
return friends;
}
public void setFriends(List<Person> friends) {
this.friends = friends;
}
}
interface DatabaseI {
}
class Database1 implements DatabaseI {
private int version;
private List<Person> people = new ArrayList<>();
public Database1() {
}
public List<Person> getPeople() {
return people;
}
public void setPeople(List<Person> people) {
this.people = people;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
class Database2 implements DatabaseI {
private String version;
private List<Person> people = new ArrayList<>();
public Database2() {
}
public List<Person> getPeople() {
return people;
}
public void setPeople(List<Person> people) {
this.people = people;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
class ToyDeserializer extends StdDeserializer<DatabaseI> {
protected ToyDeserializer(Class<?> vc) {
super(vc);
}
public ToyDeserializer() {
this(null);
}
#Override
public DatabaseI deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JacksonException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = mapper.readTree(jp);
int version = (Integer) ((IntNode) node.get("version")).numberValue();
if (version == 1) {
return mapper.treeToValue(node, Database1.class);
} else {
return mapper.treeToValue(node, Database2.class);
}
}
}
This very good question! If you want to understand why no exception is thrown, your class Person must look like this:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Person.class,
resolver = SimpleObjectIdResolverThrowsException.class
)
#JsonIdentityReference
class Person {
String id;
List<Person> friends = new ArrayList<>();
#ConstructorProperties({"id"})
public Person(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Person> getFriends() {
return friends;
}
public void setFriends(List<Person> friends) {
this.friends = friends;
}
}
class SimpleObjectIdResolverThrowsException extends SimpleObjectIdResolver {
public SimpleObjectIdResolverThrowsException() {
super();
}
#Override
public Object resolveId(ObjectIdGenerator.IdKey id) {
if (this._items == null) {
return null;
}
Object obj = this._items.get(id);
if (obj == null) {
throw new RuntimeException("Unresolved reference for: " + id);
}
return obj;
}
#Override
public ObjectIdResolver newForDeserialization(Object context) {
return new SimpleObjectIdResolverThrowsException();
}
}
Now you can set break point in the method resolveId and see what happens when we de-serialize the string "{'version':1,'people':[{'id':'a','friends':['b','c']},{'id':'b','friends':['c']},{'id':'c','friends':['b']}]}":
The problem is that the objects are processed one after the other and the references from the friends list are not resolved at that time.

How to assert/validate the JSON body and properties returned by a Micronaut controller

I am a Micronaut/Java beginner and I am trying to design some tests for my controllers. I could not find many examples online so here is my question.
Below is the controller with 2 #GET requests:
#Controller("/api/v1")
public class MyController {
private final ClientNetworkList clientNetworkList;
private final ClientStatus clientStatus;
public MyController(
ClientNetworkList clientNetworkList,
ClientStatus clientStatus
){
this.ClientNetworkList = clientNetworkList;
this.ClientStatus = clientStatus;
}
#Get(uri = "/networkList", produces = MediaType.APPLICATION_JSON_STREAM)
Flowable<NetworkListPackage> packagesNetworkList() {
return ClientNetworkList.fetchPackages();
}
#Get(uri = "/channels/{stringParm}/status/", produces = MediaType.APPLICATION_JSON_STREAM)
Flowable<ChannelStatusPackage> packagesStatus(stringParm) {
return ClientStatus.fetchPackages(genesis);
}
}
The java object POJOs:
#Introspected
public class NetworkListPackage {
private List<NetworkList> networkList = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public List<NetworkList> getNetworkList() {
return networkList;
}
public void setNetworkList(List<NetworkList> networkList) {
this.networkList = networkList;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
public class NetworkList {
private String name;
private Boolean authEnabled;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getAuthEnabled() {
return authEnabled;
}
public void setAuthEnabled(Boolean authEnabled) {
this.authEnabled = authEnabled;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
#Introspected
public class ChannelStatusPackage {
private String chaincodeCount;
private String txCount;
private String latestBlock;
private String peerCount;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getChaincodeCount() {
return chaincodeCount;
}
public void setChaincodeCount(String chaincodeCount) {
this.chaincodeCount = chaincodeCount;
}
public String getTxCount() {
return txCount;
}
public void setTxCount(String txCount) {
this.txCount = txCount;
}
public String getLatestBlock() {
return latestBlock;
}
public void setLatestBlock(String latestBlock) {
this.latestBlock = latestBlock;
}
public String getPeerCount() {
return peerCount;
}
public void setPeerCount(String peerCount) {
this.peerCount = peerCount;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
And the potential tests:
#MicronautTest
class MyControllerTest {
#Inject
#Client("/")
RxStreamingHttpClient client;
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
//when:
HttpRequest request = HttpRequest.GET("/api/v1/channels/{stringParam}/status/");
Flowable<ChannelStatusPackage> channelStatusPackageStream = client.jsonStream(request, ChannelStatusPackage.class);
Iterable<ChannelStatusPackage> channelStatusPackages = channelStatusPackageStream.blockingIterable();
//then:
//How to assert the returned body compared to the POJO?
//How to handle the parameter in the request url?
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
//when:
HttpRequest request = HttpRequest.GET("/api/v1/networkList");
Flowable<NetworkListPackage> networkListPackageStream = client.jsonStream(request, NetworkListPackage.class);
Iterable<NetworkListPackage> networkListPackages = networkListPackageStream.blockingIterable();
//then:
//How to assert the returned body and compared to the POJO?
//How to assert the returned properties ?
}
}
Based on the previous code, how can I test that the returned bodies and properties of the requests matches the POJOs?
What are the usual test to be carried out?
Thank you very much for helping.
Normaly, the basic assertion start by testing the object type, so this should validate your schema.
An other way to test it is to use RestAssured, witch is a bit more readable.
You need to import the fallowing dependencies in you build.gradle
testImplementation("io.rest-assured:rest-assured:4.2.+")
testImplementation("io.rest-assured:json-schema-validator:4.2.+")
You need test annotation processor to enable micronaut injection and junit 5 for the BeforeEach.
The full test dependencies:
testAnnotationProcessor("io.micronaut:micronaut-inject-java")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testImplementation("io.rest-assured:rest-assured:4.2.+")
testImplementation("io.rest-assured:json-schema-validator:4.2.+")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
Then you can wright your tests like that:
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import io.micronaut.http.HttpStatus;
import io.micronaut.runtime.server.EmbeddedServer;
import io.micronaut.test.annotation.MicronautTest;
import io.restassured.RestAssured;
import javax.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
#MicronautTest
class MyControllerTest {
#Inject
private EmbeddedServer embeddedServer;
#BeforeEach
public void setUp() {
RestAssured.port = embeddedServer.getPort();
}
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.pathParam("stringParam", "value")
.get("/api/v1/channels/{stringParam}/status/")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(
"chaincodeCount", equalTo("chaincodeCountValue"),
"txCount", equalTo("txCountValue"),
"latestBlock", equalTo("latestBlockValue"),
"peerCount", equalTo("peerCountValue"),
"additionalProperties.key1", equalTo("additionalPropertyValue1"),
"additionalProperties.key2", equalTo("additionalPropertyValue2")
);
}
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.get("/api/v1/networkList")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(
"networkList.name[0]", equalTo("nameValue0"),
"networkList.authEnabled[0]", equalTo("authEnabledValue0"),
"networkList.additionalProperties[0].key1", equalTo("additionalPropertiesValue1"),
"networkList.additionalProperties[0].key2", equalTo("additionalPropertyValue2")
);
}
}
This is not really the way you wanted to do your tests, but I hope it will help.
So I ended up using the "hasItems" matcher or/and the jackson schema matcher.
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import io.micronaut.http.HttpStatus;
import io.micronaut.runtime.server.EmbeddedServer;
import io.micronaut.test.annotation.MicronautTest;
import io.restassured.RestAssured;
import javax.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.hasItems;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
#MicronautTest
class MyControllerTest {
#Inject
private EmbeddedServer embeddedServer;
#BeforeEach
public void setUp() {
RestAssured.port = embeddedServer.getPort();
}
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.pathParam("stringParam", "value")
.get("/api/v1/channels/{stringParam}/status/")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(matchesJsonSchemaInClasspath("channelsStatus.json"))
.body("keySet()",hasItems(
"chaincodeCount",
"txCount",
"latestBlock",
"peerCount",
);
}
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.get("/api/v1/networkList")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(matchesJsonSchemaInClasspath("networkList.json"))
.body("networkList.keySet()",hasItems(
"name",
"authEnabled",
);
}
}
``
Another option is to use jsonPath similar to Spring Boot MockMvc ResultMatcher:
testImplementation 'com.jayway.jsonpath:json-path:2.4.0'
testImplementation 'org.hamcrest:hamcrest:2.2'
Get the response as HttpResponse<String> and then use JsonPath.parse(response.body()) to assert the json path:
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
URI uri = UriBuilder.of("/api/v1/channels/{stringParam}/status/").expand(singletonMap("stringParam", "value"));
HttpResponse<String> response = client.toBlocking().exchange(HttpRequest.GET(uri), String.class);
assertEquals(HttpStatus.OK, response.getStatus());
ReadContext ctx = JsonPath.parse(response.body());
assertThat(ctx.read("$"), isA(Object.class));
assertThat(ctx.read("$.chaincodeCount"), is("chaincodeCountValue"));
}
Example for an endpoint test using Micronaut vs Spring Boot

Xml is working fine but json is not in jax rs

I am learning restful api with jax rs(jersey) and I making a messageResource class in which I am getting messages .everything works fine with xml but get function is not working with json .I have tried content type and accept headers too but its not working and returning 500 error and content type text/html.
Even the maven dependency moxy is also set in pom.xml
Can anybody help me finding the error.
package org.restfulapi.messenger.resources;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
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.UriInfo;
import org.restfulapi.messenger.services.DataNotFoundException;
import org.restfulapi.messenger.services.Link;
import org.restfulapi.messenger.services.Message;
import org.restfulapi.messenger.services.MessageService;
#Path("messages")
#Produces(value= {MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
#Consumes(value= {MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public class MessageResource
{
MessageService service=new MessageService();
#GET
#Path("/{messageID}")
public Message getMessage(#PathParam("messageID")int id,#Context UriInfo uriinfo )
{
Message msg=service.getMessage(id);
if(service.getMessage(id)==null)
{
throw new DataNotFoundException("message with id "+id+"not found");
}
return msg;
}
#GET
public List<Message> getMessages(#QueryParam("year")int year,
#QueryParam("start") int start,
#QueryParam("size") int size)
{
if(year>0)
{
return service.getbyyear(year);
}
if(start>0 & size>0)
{
return service.get(start, size);
}
return service.getMessages();
}
#POST
public Response addMessage(Message msg) throws URISyntaxException
{
return Response.created(new URI("/messenger/webapi/messages"+msg.getId())).entity(service.addMessage(msg)).build();
}
package org.restfulapi.messenger.services;
import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
public class MessageService {
private HashMap<Integer, Message> messages = Database.getMessages();
public MessageService()
{
}
public ArrayList<Message> getMessages() {
return new ArrayList<Message>(messages.values());
}
;
public Message getMessage(int id) {
return messages.get(id);
}
public ArrayList<Message> getbyyear(int year)
{
ArrayList<Message> result =new ArrayList();
Calendar cal=Calendar.getInstance();
for(Message msg:messages.values())
{
cal.setTime(msg.getCreated());
if(cal.get(Calendar.YEAR)==year)
result.add(msg);
}
return result;
}
public List<Message> get(int start,int size)
{
ArrayList<Message> result =new ArrayList<Message>(messages.values());
return result.subList(start, start+size);
}
public Message addMessage(Message message) {
message.setId(messages.size() + 1);
messages.put(message.getId(), message);
return message;
}
}
package org.restfulapi.messenger.services;
import java.util.ArrayList;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Message {
private int id;
private String msg;
private Date created;
private String author;
private ArrayList<Link> links=new ArrayList();
public ArrayList<Link> getLinks() {
return links;
}
public void setLinks(ArrayList<Link> links) {
this.links = links;
}
public Message()
{
}
public Message(int id, String msg, String author) {
this.id = id;
this.msg = msg;
this.author = author;
this.created = new Date();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public void addLink(String uri, String rel) {
Link l=new Link(uri,rel);
links.add(l);
}
}
public class Database
{
private static HashMap<Integer,Message> messages=new HashMap();
public static HashMap<Integer,Message> getMessages()
{
return messages;
}
}

Spring-data-solr config

i met a problem in Studying with Spring data solr,this is my Configuration Class:
#Configuration
#EnableSolrRepositories(basePackages={"cn.likefund.solr.repository"}, multicoreSupport=true)
public class SolrContext {
static final String SOLR_HOST = "http://192.168.11.157:8080/solr";
#Bean
public SolrClient solrClient() {
return new HttpSolrClient(SOLR_HOST);
}
}
and this is my Repository:
package cn.likefund.solr.repository;
import java.util.List;
import org.springframework.data.solr.repository.SolrCrudRepository;
import cn.likefund.solr.model.Activity;
public interface ActivityRepository extends SolrCrudRepository<Activity, String>{
List<Activity> findByName(String name);
}
when I start the application,the message in console is this
error
When I delete the method findByName in the repository,the application start with no problem, i just want to the method findByName worked,anybody know what should i do with this problem?
here is the Activity Class:
#Entity
#SolrDocument(solrCoreName ="core_activity")
public class Activity implements Serializable{
private static final long serialVersionUID = 1566434582540525979L;
#Id
#Field(value = "id")
private String id;
#Field(value = "CREATEDT")
private String createdt;
#Indexed
#Field(value = "NAME")
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCreatedt() {
return createdt;
}
public void setCreatedt(String createdt) {
this.createdt = createdt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
So, obviously the CrudRepository is not created .
when you delete the findByName, can you manually query your repo ? (just to be sure the problem comes from the method, and not the SOLR schema)
have you tried to annotate annotate the method to explicitly set the query ? Something like
#Query("NAME:?0")
List findByName(String name);

Unable to log in to Apache FTP Server

I am trying to integrate the Apache FTP server into my application. I have followed the instructions given here but have run into some problems. Currently I am able to run the server and connect to it from my browser but can not log in. I have tried admin/admin and anonymous/*, but the login fails every time. In the apache-ftpserver-1.0.6 source code I had downloaded, the files associated with the user manager are located in res/conf, although when I try to match that file path in my own program I get an error that says "invalid resource directory name" and am unable to build. I also tried including the files users.properties and ftpd-typical.xml in the main directly and can run, but again cannot log in. It seems like my project does not realize these files are present.
Does anyone have experience with Apache FTP Server that could tell me the correct way to include these files so that I can log in to my server?
Thanks!
P.S. I don't think it should make any difference, but I am developing this program for Android.
In the following code I am crating admin user and non-admin user, setting restrictions of reading, writing and restricting throttling and upload rate limit and imposing download rate limiting.
Added a listener to listen user login and logout download start and download finish events.
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.FileSystemFactory;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.Ftplet;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.ftpletcontainer.impl.DefaultFtpletContainer;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
import org.apache.ftpserver.usermanager.SaltedPasswordEncryptor;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.ConcurrentLoginPermission;
import org.apache.ftpserver.usermanager.impl.TransferRatePermission;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SFTPServer {
// ===========================================================
// Constants
// ===========================================================
private final int FTP_PORT = 2221;
private final String DEFAULT_LISTENER = "default";
// private final Logger LOG = LoggerFactory.getLogger(SFTPServer.class);
private static final List<Authority> ADMIN_AUTHORITIES;
private static final int BYTES_PER_KB = 1024;
private static final String DEFAULT_USER_DIR = "C:\\upload";
public final static int MAX_CONCURRENT_LOGINS = 1;
public final static int MAX_CONCURRENT_LOGINS_PER_IP = 1;
// ===========================================================
// Fields
// ===========================================================
private static FtpServer mFTPServer;
private static UserManager mUserManager;
private static FtpServerFactory mFTPServerFactory;
private ListenerFactory mListenerFactor;
// ===========================================================
// Constructors
// ===========================================================
static {
// Admin Authorities
ADMIN_AUTHORITIES = new ArrayList<Authority>();
ADMIN_AUTHORITIES.add(new WritePermission());
ADMIN_AUTHORITIES.add(new ConcurrentLoginPermission(MAX_CONCURRENT_LOGINS, MAX_CONCURRENT_LOGINS_PER_IP));
ADMIN_AUTHORITIES.add(new TransferRatePermission(Integer.MAX_VALUE, Integer.MAX_VALUE));
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
public void init() throws FtpException {
mFTPServerFactory = new FtpServerFactory();
mListenerFactor = new ListenerFactory();
mListenerFactor.setPort(FTP_PORT);
mFTPServerFactory.addListener(DEFAULT_LISTENER, mListenerFactor.createListener());
mFTPServerFactory.getFtplets().put(FTPLetImpl.class.getName(), new FTPLetImpl());
PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
userManagerFactory.setFile(new File("ftpusers.properties"));
userManagerFactory.setPasswordEncryptor(new SaltedPasswordEncryptor());
mUserManager = userManagerFactory.createUserManager();
mFTPServerFactory.setUserManager(mUserManager);
this.createAdminUser();
SFTPServer.addUser("admin1", "admin1", 20, 20);
mFTPServer = mFTPServerFactory.createServer();
mFTPServer.start();
}
private UserManager createAdminUser() throws FtpException {
UserManager userManager = mFTPServerFactory.getUserManager();
String adminName = userManager.getAdminName();
if (!userManager.doesExist(adminName)) {
// LOG.info((new
// StringBuilder()).append("Creating user : ").append(adminName).toString());
BaseUser adminUser = new BaseUser();
adminUser.setName(adminName);
adminUser.setPassword(adminName);
adminUser.setEnabled(true);
adminUser.setAuthorities(ADMIN_AUTHORITIES);
adminUser.setHomeDirectory(DEFAULT_USER_DIR);
adminUser.setMaxIdleTime(0);
userManager.save(adminUser);
}
return userManager;
}
public static void addUser(String username, String password, int uploadRateKB, int downloadRateKB) throws FtpException {
BaseUser user = new BaseUser();
user.setName(username);
user.setPassword(password);
user.setHomeDirectory(DEFAULT_USER_DIR);
user.setEnabled(true);
List<Authority> list = new ArrayList<Authority>();
list.add(new TransferRatePermission(downloadRateKB * BYTES_PER_KB, uploadRateKB * BYTES_PER_KB)); // 20KB
list.add(new ConcurrentLoginPermission(MAX_CONCURRENT_LOGINS, MAX_CONCURRENT_LOGINS_PER_IP));
user.setAuthorities(list);
mFTPServerFactory.getUserManager().save(user);
}
public static void restartFTP() throws FtpException {
if (mFTPServer != null) {
mFTPServer.stop();
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
}
mFTPServer.start();
}
}
public static void stopFTP() throws FtpException {
if (mFTPServer != null) {
mFTPServer.stop();
}
}
public static void pauseFTP() throws FtpException {
if (mFTPServer != null) {
mFTPServer.suspend();
}
}
public static void resumeFTP() throws FtpException {
if (mFTPServer != null) {
mFTPServer.resume();
}
}
public static void main(String... are) {
try {
new SFTPServer().init();
} catch (FtpException e) {
e.printStackTrace();
}
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}
FTPLET Listener
import java.io.IOException;
import org.apache.ftpserver.ftplet.DefaultFtplet;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.FtpRequest;
import org.apache.ftpserver.ftplet.FtpSession;
import org.apache.ftpserver.ftplet.FtpletResult;
public class FTPLetImpl extends DefaultFtplet {
#Override
public FtpletResult onLogin(FtpSession session, FtpRequest request) throws FtpException, IOException {
System.out.println(session.getUser().getName() + " Logged in");
return super.onLogin(session, request);
}
#Override
public FtpletResult onDisconnect(FtpSession session) throws FtpException, IOException {
System.out.println(session.getUser().getName() + " Disconnected");
return super.onDisconnect(session);
}
#Override
public FtpletResult onDownloadStart(FtpSession session, FtpRequest request) throws FtpException, IOException {
System.out.println(session.getUser().getName() + " Started Downloading File " + request.getArgument());
return super.onDownloadStart(session, request);
}
#Override
public FtpletResult onDownloadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
System.out.println("Finished Downloading " + request.getArgument());
return super.onDownloadEnd(session, request);
}
}